JSONB - update array value by index - servicestack

How to update JSON(B) array value by index? And also to retrieve the index of each value in JSONB array?
There is ServiceStack ORMLite model:
public class Page
{
[AutoIncrement]
public long Id { get; set; }
[PgSqlJsonB]
public List<Widget> Widgets { get; set; }
}
For example, how to update the second item in Widgets list?
Here is an example of how to do select array indexes and update array value by index in raw Postgres SQL:
How to update objects inside JSONB
The idea is to select array indexes with AutoQuery and update particular JSONB array value knowing it's index in database array.

In OrmLite complex Types like List<Widget> are blobbed, so if you change the value in C# and save it, it will serialize the entire Widgets property to JSON and update the entire field.
If you want to use PostgreSQL native functions to manipulate the column contents in a server side query you'd need to use the Custom SQL APIs, e.g:
db.Execute("UPDATE page SET widgets = jsonb_set(widgets, ...) WHERE id = #id",
new { id });

Related

Cosmos Document query to locate latest record per type

I have a collection where I am storing the timestamp and its latest location with the following class:
public class TrackingInfo
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("_partition_key")]
public string _PartitionKey { get; set; }
[JsonProperty("asset_id")]
public string AssetId { get; set; }
[JsonProperty("unix_timestamp")]
public double UnixTimestamp { get; set; }
[JsonProperty("timestamp")]
public string Timestamp { get; set; }
[JsonProperty("location")]
public Point Location { get; set; }
}
which is partitioned by _PartitionKey which contains a construct like this:
tracking._PartitionKey = $"tracking_{tracking.AssetId.ToLower()}_{DateTime.Today.ToString("D")}";
Looks like there is no way to do a Group by on the collection.
Can someone please help me create a SQL document query to find the latest entry for each AssetId and its Location and Timnestamp when the data was recorded.
Update 1:
what if I change the _PartitionKey to represent per day something like below:
tracking._PartitionKey = $"tracking_{DateTime.Today.ToString("D")}";
would it make it easier to get all assets and its latest tracking record?
As per my comment, my suggestion would be to solve your problem differently.
Assumption: You have a large number of assetIds and don't know the values beforehand:
Have one document that represents the latest state of your asset
Have another document that represents the location events of your asset
Update the first document whenever there is a new location event
You can put both types of documents in the same collection or separate them - both approaches have benefits. I would probably separate them.
Then do a query "what assets are within 1km of xxx" (Querying spatial types)
Sidenote: It might be a good idea to use the assetId as partitionKey instead of your combined key. Using such a key is very bad for queries
If you only have very few assetIds, you can use those to only find the latest updates by using and ordering by the timestamp field. This will only return the last item
Cosmos DB doesn't support group by feature,you could vote up this.
Provide a third-party package [documentdb-lumenize for your reference which supports group by feature,it has .net example:
string configString = #"{
cubeConfig: {
groupBy: 'state',
field: 'points',
f: 'sum'
},
filterQuery: 'SELECT * FROM c'
}";
Object config = JsonConvert.DeserializeObject<Object>(configString);
dynamic result = await client.ExecuteStoredProcedureAsync<dynamic>("dbs/db1/colls/coll1/sprocs/cube", config);
Console.WriteLine(result.Response);
You could group by assetId column and get the max timestamp.

Parameter lengths for parameterized queries in OrmLite

A POCO update in OrmLite executes SQL like this example:
(#P1 varchar(1043),#P2 varchar(6))
UPDATE table
SET FILEDATA=#P1
WHERE FILEID=#P2
But it leads to multiple query plans based on different #P1 and #P2 values with varying parameter lengths.
So, what's the best way(s) to specify data types/lengths for parameterized queries in Ormlite, so that query plans are cached properly, and avoids multiple query plans due to variable parameter lengths?
Here's a similar situation with having variable length strings: https://dba.stackexchange.com/questions/216330/parameterized-query-creating-many-plans
Update
Here's an example:
Database Table
dbo.Users
Id (PK, int, not null)
Email (nvarchar(150), not null)
POCO
[Alias("Users")]
public class User
{
[PrimaryKey]
[AutoIncrement]
public int Id { get; set; }
public string Email { get; set; }
}
Code
int userId = 1;
User user;
// get User
using (var db = DbConn.OpenDbConnection())
{
user = db.SingleById<User>(userId);
}
// print User email (hi#example.com)
Console.WriteLine(user.Email);
// update User email
using (var db = DbConn.OpenDbConnection())
{
user.Email = "tester#example.org";
db.Update(User);
}
The update operation will result in an SQL query similar to the one I've posted at the top, with variable length of parameters. It causes multiple query plans to be created by SQL Server due to variable length of parameters. Ideally, the query should have fixed length of parameters, so that a query plan can be created, cached and reused for the same operations (e.g. User update) with varying parameter values (i.e. different email).
The Size of string parameters are now being specified from this commit where it takes the default string size of the configured StringConverter. This change is available from v5.5.1 that's now available on MyGet.
If needed its behavior can be overridden by replacing the String Converter and overriding InitDbParam().

ServiceStack OrmLite - Handle Auto increment column default seed

How can i set a auto increment column seed from 1 to 100??
in sql server can do this use
ADD Id INT NOT NULL IDENTITY(1000,1)
but in ormlite autoincrement attribute seems like always start with 1.
tried also
db.AlterTable<MSchool>("command") // DROP ID AND ADD AUTO INCREMENT COLUMN
it works, if Id doesn't related to any table.
can i set a column autoincrement with default seed and increment?
[AutoIncrement(1000,1)]
public int Id {get;set;}
UPDATE
Resolved, but not good
public class School
{
[AutoIncrement]
public int Id {get;set;}
}
//then create table
db.CreateTable<School>();
//then update seed
db.ExecuteSql("DBCC CHECKIDENT ('School',reseed,1000)");
OR
[PostCreateTable("DBCC CHECKIDENT ('School',reseed,1000)")]
public class School : BaseModel
{
[AutoIncrement]
public int Id {get;set;}
}
Is there no easier way to do this??
Personally I don't believe this behavior belongs inside source code and would just modify the database out-of-band.
But if I were to add it in source code I'd do something like:
if (db.CreateTableIfNotExists<School>())
{
db.ExecuteSql("DBCC CHECKIDENT ('School',reseed,1000)");
}
So it only resets the seed if the table doesn't exist.
Another option is attach it to the model at runtime so it's decoupled from the class definition with:
typeof(School)
.AddAttributes(new PostCreateTableAttribute(
"DBCC CHECKIDENT ('School',reseed,1000)"));

servicestack ormlite partial update

I'm using ServiceStack Ormlite to do partial update to a database table.
I have a model:
public class Model
{
public int Id;
public int Property1;
public int Property2;
public int Property3;
}
But I only want to update fields Property1, and Property2.
Does anybody know how to do this?
Thanks.
See ServiceStack's OrmLite documentation for Update statements - they contain many different different examples of partial updates.
Here is what an ServiceStack OrmLite multiple field update with where clause looks like:
Db.UpdateOnly(
new Table_DTO_Object { Field_1 = Val_1, Field_2 = Val_2, Field_3 = Val_3 },
obj => new { obj.Field_1, obj.Field_2, obj.Field_3 },
obj => obj.Id == objId);
How to update multiple fields on a single table row / with a where clause is not immediately apparent from the ServiceStack documentation because they don't have an example with both 1) multiple fields and 2) where clause.
They have an example that updates multiple fields and they have an example of an update with a where clause - really all you need to do / I did is take the needed functionality from each example.

DDD: Modeling simple domain with two aggregate roots

Let's say I want to create action web site where members would be able to bid for items. To model this domain I have three classes: Member, Item and Bid.
My brainstorming would go something like this:
Item can contain multiple bids
Bid is associated with one Item and one Member
Member can contain multiple bids
Member and Item can exist without bid instance
Bid instance can't exist without both Member and Item
Considering all this it is obvious that since Member and Item objects are independent we can consider them aggregate roots. Bid will be part of one of these aggregate. That is clear but what is confusing to me right now is which aggregate root should I choose? Item or Member?
This is example from Pro ASP.NET MVC 3 Framework book by Apress, and the way they did is like following:
Which gives following code:
public class Member
{
public string LoginName { get; set; } // The unique key
public int ReputationPoints { get; set; }
}
public class Item
{
public int ItemID { get; private set; } // The unique key
public string Title { get; set; }
public string Description { get; set; }
public DateTime AuctionEndDate { get; set; }
public IList<Bid> Bids { get; set; }
}
public class Bid
{
public Member Member { get; set; }
public DateTime DatePlaced { get; set; }
public decimal BidAmount { get; set; }
}
Member and Item are aggregate roots here and Bid is contained within Item.
Now let's say that I have application use case: "Get all bids posted by specific member". Does that mean that I would have to first get all Items (eg. from data base via repository interface) and then enumerate all bids for each Item trying to find matching Member? Isn't that a bit inefficient? So a better way would be then to aggregate Bid objects inside of Member. But in that case consider the new use case: "Get all bids for specific item". Now again we need to go other way around to get all bids...
So taking into account that I need to implement both of these use cases in my application, what is the right and efficient way to model this domain then?
Your domain should really reflect only Command (CQRS) requirements (update/change data). I presume that you need Queries (read data, no update/change of data): "Get all bids for specific item" and "Get all bids posted by specific member". So, this "querying" has nothing to do with the domain, as the query implementation is independent on the command implementation (command is calling a domain method). This gives you a freedom to implement each query in an efficient way. My approach is to implement an efficient DB view getting you only data you want to display in UI. Then you create a new class called BidForItemDto (DTO = data transfer object) and you map data from DB view into a collection of BidForItemDto (you can do it manually via ADO.NET or use NHibernate (preferred, does everything for you)). The same for the second query, create a new class called BidPostedByMemberDto.
So, if it is queries you need, just forget about domain, realize that it's just data you want to display in UI, and query them efficiently from the DB. Only when you do some action in UI (click a button to place a bid for instance), it's executing a command "place a bid", which would at the end call domain method Item.PlaceBid(Member member, DateTime date, decimal amount). And btw, IMHO is it an Item which "has many bids", and the domain method "place bid" would surely need to access previous bids to implement the whole logic correctly. Placing bids collection into Member does not make much sense to me...
From the top of my head some examples of DB views and sql queries:
Get all bids for specific item:
create view BidForItemDto
as
select
i.ItemId,
b.BidId,
b.MemberId,
b.DatePlaced,
b.BidAmount
from Item i
join Bid b ON b.ItemId = i.ItemId
query:
SELECT *
from BidFormItemDto
where ItemId = <provide item id>
Get all bids posted by specific member:
create view BidPostedByMemberDto
as
select
m.MemberId,
b.BidId,
b.MemberId,
b.DatePlaced,
b.BidAmount
from Member m
join Bid b ON b.MemberId = i.MemberId
query:
SELECT *
from BidPostedByMemberDto
where MemberId = <provide member id>

Resources