CosmosDB SQL JavaSDK: Data model for select distinct - azure-cosmosdb

I have a CosmosDB data model, where each document is like this:
{
"someList": [
{
"field1": "value1",
"field2": "value2"
}
],
"date": "2022-08-31"
}
My goal is to have a query to get distinct combination of field1 and field2 from db for all documents on a particular date with the following SQL:
SELECT DISTINCT f.field1 as field1, f.field2 as field2
FROM c join (
SELECT value t from t in c.someList
) as f where c.date='2022-08-31'
My Java data model:
import lombok.*;
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Data {
private String field1;
private String field2;
}
When I tried to query with the below code:
CosmosPagedFlux<Data> pagedFlux = container.queryItems(
sqlStatementAbove, Data.class
);
I got a stacktrace like below:
07:57:21.805 ERROR reactor.Mono.Map.64 - onError(java.lang.IllegalArgumentException: Unexpected type: class xxxxxxx.Data)
07:57:21.805 ERROR reactor.Mono.Map.64 -
java.lang.IllegalArgumentException: Unexpected type: class xxxxxxx.Data
at com.azure.cosmos.implementation.query.DistinctHash.getHash(DistinctHash.java:85)
at com.azure.cosmos.implementation.query.DistinctHash.getHash(DistinctHash.java:39)
at com.azure.cosmos.implementation.query.UnorderedDistinctMap.add(UnorderedDistinctMap.java:29)
at com.azure.cosmos.implementation.query.DistinctDocumentQueryExecutionContext.lambda$drainAsync$1(DistinctDocumentQueryExecutionContext.java:88)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
I noticed that if I remove DISTINCT from the query, then it's OK. So how do I make my data model to work with DISTINCT queries?

Related

How to select DISTINCT entries in SQLite Flutter with whereArgs?

I want to return Distinct records from a table in SQLITE flutter by giving some condition such as return All the distinct records where ID = x (where x will be taken by the user).
How can I achieve this?
Working with the SQFlite package you can use a query or a rawQuery to perform that, in both cases you need to pass the where arguments as an array, and for the distinct operator you can do it as follows:
/// [GET] Get all the data by date using a query
getData(String date) async {
final db = await database;
final response = await db.query(
"TableExample",
distinct: true,
orderBy: 'id',
where: 'date = ?',
whereArgs: [date],
);
//...
}
/// [GET] Get all the data by date using a rawQuery
getDailyPollResponsesPerRangeDate(String date) async {
final db = await database;
final response = await db.rawQuery(
"SELECT DISTINCT TableExample.id, TableExample.date "
"FROM TableExample "
"WHERE TableExample.date == ? "
"ORDER BY TableExample.date DESC",
[date],
);
//...
}

How to automatically set timestamp in room SQLite database?

I am trying to have SQLite create automatic timestamps with CURRENT_TIMESTAMP.
I took the liberty of using Google's code:
// roomVersion = '2.2.2'
#Entity
public class Playlist {
#PrimaryKey(autoGenerate = true)
long playlistId;
String name;
#Nullable
String description;
#ColumnInfo(defaultValue = "normal")
String category;
#ColumnInfo(defaultValue = "CURRENT_TIMESTAMP")
String createdTime;
#ColumnInfo(defaultValue = "CURRENT_TIMESTAMP")
String lastModifiedTime;
}
#Dao
interface PlaylistDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(playlist: Playlist): Long
}
This translates into an SQLite-Statement:
CREATE TABLE `Playlist` (
`playlistId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` TEXT,
`description` TEXT,
`category` TEXT DEFAULT 'normal',
`createdTime` TEXT DEFAULT CURRENT_TIMESTAMP,
`lastModifiedTime` TEXT DEFAULT CURRENT_TIMESTAMP
)
I did make one insert:
mDb.playListDao().insert(Playlist().apply { name = "Test 1" })
But the timestamps are always Null.
With the DB Browser for SQLite I added another entry, here I get timestamps.
How do I insert without a Null-Timestamp in room?
(Info: createdTime is also always the same as lastModifiedTime. I think this has to be done with triggers in SQLite, but that is a different problem not to be discussed here).
You don't need to use another class, you can use #Query as an alternative to the convenience #Insert.
as per :-
There are 4 type of statements supported in Query methods: SELECT, INSERT, UPDATE, and DELETE.
Query
e.g.
#Query("INSERT INTO test_table001 (name) VALUES(:name) ")
void insert(String name);
You are also not limited to CURRENT_TIMESTAMP as the only means of getting the current timestamp you can use embedded datetime functions (as is shown below), which can store the value more efficiently and also be more flexible e.g. you could adjust the current time using modifiers such as '+7 days'.
If you consider the following :-
#Entity(tableName = "test_table001")
public class TestTable001 {
#PrimaryKey
Long id;
#ColumnInfo(defaultValue = "CURRENT_TIMESTAMP")
String dt1;
#ColumnInfo(defaultValue = "(datetime('now'))")
String dt2;
#ColumnInfo(defaultValue = "(strftime('%s','now'))")
String dt3;
String name;
}
Note that the inefficient autogenerate = true has not been used BUT as will be shown you can still have an SQLite assigned id (note that you must use the type Long/Integer as opposed to long or int)
Also note the alternative ways of getting the current date time (the latter being more efficient as the value will ultimately be stored as an Integer (max 8 bytes) rather than a more byte hungry String).
With a Dao as :-
#Dao
public interface TestTable001Dao {
#Insert()
long insert(TestTable001 testTable001);
#Query("INSERT INTO test_table001 (name) VALUES(:name) ")
long insert(String name);
#Query("SELECT * FROM test_table001")
List<TestTable001> getAllTestTable001();
}
And the following to test/demonstrate :-
public class MainActivity extends AppCompatActivity {
AppDatabase mRoomDB;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRoomDB = Room.databaseBuilder(this,AppDatabase.class,"testdb")
.allowMainThreadQueries()
.build();
TestTable001 tt01 = new TestTable001();
tt01.setName("tt01");
mRoomDB.useTestTable001().insert(tt01);
mRoomDB.useTestTable001().insert("tt02");
logAllTestTable001();
}
private void logAllTestTable001() {
for (TestTable001 tt: mRoomDB.useTestTable001().getAllTestTable001()) {
Log.d(
"TTINFO",
"ID = " + tt.getId() +
" Name = " + tt.getName() +
" Date1 = " + tt.getDt1() +
" Date2 = " + tt.getDt2() +
" Date3 = " + tt.getDt3());
}
}
}
The result is :-
2019-12-14 03:18:32.569 D/TTINFO: ID = 1 Name = tt01 Date1 = null Date2 = null Date3 = null
2019-12-14 03:18:32.569 D/TTINFO: ID = 2 Name = tt02 Date1 = 2019-12-13 16:18:32 Date2 = 2019-12-13 16:18:32 Date3 = 1576253912
Found it. Did not read the manual.
You have to create a 2nd class without the auto-set fields to insert.
public class NameAndDescription {
String name;
String description
}
I think, this is not a good idea.
If you have an autoincrement field in the DB it will get an automatically updated value when you pass 0.
Likewise the default value of the timestamp should be used when passing null or "".
I found the best solution was creating an abstract Dao that implemented the insert and update methods. I didn't get the default value to work (perhaps I was doing something wrong). Take a look at my answer here: How to implement created_at and updated_at column using Room Persistence ORM tools in android

How Can I Solve This Problem in my dart code sqlite

I have a problem in my code that occurs error in lunching in database_helper.dart
database_helper.dart is one file in Sqlite storing data learning project and i faced this issue ,
i tried many solutions like uninstall app and install it again
can any one help me ?
======
class DataBaseHelper {
static Database _db ;
final String userT = 'userT' ;
final String columnId = 'id' ;
final String columnUserName = 'username' ;
final String columnPassword = 'password' ;
final String columnAge = 'age' ;
final String columnCity = 'city' ;
=========
Future<Database> get dbase async {
if(_db != null) {
return _db ;
}
_db = await intDB();
return _db ;
}
=================
intDB() async {
Directory docDirectory = await getApplicationDocumentsDirectory() ;
String path = join(docDirectory.path , 'myDB.db') ;
var myOwnDB = await openDatabase(path , version: 1 , onCreate: _onCreate);
return myOwnDB ;
}
=========================
void _onCreate(Database db , int newVersion) async {
var sql = 'CREATE TABLE $userT ($columnId INTEGER PRIMARY KEY UNIQUE ,'
' $columnUserName TEXT , $columnPassword TEXT , $columnCity TEXT , $columnAge INTEGER ' ;
await db.execute(sql) ;
}
error log:
E/SQLiteLog(14607): (1) near "INTEGER": syntax error
I/flutter (14607): error DatabaseException(near "INTEGER": syntax error (code 1): , while compiling: CREATE TABLE userT (id INTEGER PRIMARY KEY UNIQUE , username TEXT , password TEXT , city TEXT , age INTEGER) during oenter code herepen, closing...
E/flutter (14607): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: DatabaseException(near "INTEGER": syntax error (code 1): , while compiling: CREATE TABLE userT (id INTEGER PRIMARY KEY UNIQUE , username TEXT , password TEXT , city TEXT , age INTEGER)
Syntax error. you forgot to put ) at the end of line
$columnAge INTEGER )
You can check your sqllite code in https://sqliteonline.com/
CREATE TABLE userT (columnId INTEGER PRIMARY KEY UNIQUE ,
columnUserName TEXT , columnPassword TEXT , columnCity TEXT , columnAge INTEGER)
after put ) . it works fine.
Edit : Second question of Insert Into statement you mentioned in comments
The syntax error of Insert Into statement is column "userCity" does not exist.
In the question you provided, when Create table you use the name columnCity = 'city'. so you sholud change the name to "city".
Try removing UNIQUE
They have not included that word in any of the samples, so maybe it has not been implemented.

JdbcTemplate to batchUpdate to multiple tables at same time

JdbcTemplete.batchUpdate() can take a prepared statement and can fire off a number of inserts to the same table.
String sql = "INSERT INTO MYTABLE (COL1, COL2) VALUES (?, ?)"
List params = ...
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
List<String> singleRowParams = params.get(i);
ps.setString(1, singleRowParams.get(0));
ps.setString(2, singleRowParams.get(1));
}
// This is the number of times to run the SQL statement.
public int getBatchSize() {
return params.size();
}
}
);
How do I insert into mutliple tables in the one batch update, is that even possible?
Thanks
No it's not possible. Think about if you were trying to run this SQL manually, how would you go about doing it? An alternative would be to iterate over your updates and amend the SQL each time for the relevant table(s).

Insert record using entity Framework (database first)

there are 3 database tables (movies, reviews, users)
the reviews table include ( MemeberID, MovieID, Review Text, Rate, ReviewDate)
(the MemeberID, and MovieID in the Review are the FK of the members table and the movies table)
The Movie can have many reviews, and i'm trying to add review to a movie
even I have movie class and the member class, I have a problem, in order to insert review, i need to reference it to movie and users , link them, and i don't know how to do it
this code make a error:
" The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects. "
This is my code...
public bool InsertNewReview(Movie _TheMovie, Member _TheMember, string _Text, byte _Rate, DateTime _ReviewDate)
{
Review ReviewToInsert = new Review()
{
MovieID = _TheMovie.MovieID,
MemberID = _TheMember.MemberID,
Movie = _TheMovie,
Member = _TheMember,
Rate = _Rate,
ReviewDate = _ReviewDate,
ReviewText = _Text
};
videoLib.Reviews.AddObject(ReviewToInsert);
videoLib.SaveChanges();
return true;
}
..
there are more data to insert to the Review class
Images: here
..
and the tables: (the "all columns" isn't a field in database tables)
Images: here
could you try like this
Review ReviewToInsert = videoLib.Reviews.CreateObject();
ReviewToInsert.MovieID = _TheMovie.MovieID
...
...
videoLib.Reviews.AddObject(ReviewToInsert);
videoLib.SaveChanges();
I got a solution, I need to define only the MovieID, MemberID, and not using their object
and use try & catch, to detect if thier the same MovieID (fk) and MemberID (fk) in the same row (because the review don't have is own id in the database)
public bool InsertNewReview(string _MovieID, int _MemberID, string _Text, byte _Rate, DateTime _ReviewDate)
{
try
{
Review ReviewToInsert = new Review()
{
Rate = _Rate,
ReviewDate = _ReviewDate,
ReviewText = _Text,
MovieID = _MovieID,
MemberID = _MemberID
};
videoLib.Reviews.AddObject(ReviewToInsert);
videoLib.SaveChanges();
return true;
}
catch
{
return false;
}
}

Resources