Why many to one with backlink not allowed in objectbox-dart? - flutter

I have this structure:
#JsonSerializable()
class Media {
#Id(assignable: true)
int id = 0;
int lid = 0;
String url = '';
String? title;
}
#Entity()
class NewsPicture extends Media {
#override
#Id(assignable: true)
int id = 0;
#Backlink('newsPicture')
final news = ToOne<News>();
}
#JsonSerializable(explicitToJson: true)
#Entity()
class News extends Data<News> implements DataInterface<News> {
#Id(assignable: true)
#JsonKey(defaultValue: 0)
int lid = 0;
final Picture = ToMany<NewsPicture>();
}
and at the generation process, the objectbox_generator:resolver gives me this error message:
invalid use of #Backlink() annotation - may only be used on a ToMany<>
field
Why is it not allowed? What am I missing?

It's not supported in that direction because it's not really needed (doesn't help with anything), you can just flip the direction/change the place where the relation is stored. Also, ToOne relations are more efficient to store, because they're just a single field in a database, while a standalone ToMany relations require an intermediate "mapping table".
If you update your model like this, it's going to work and you won't see a difference in the way how you can work with it in your app:
#Entity()
class NewsPicture extends Media {
#override
#Id(assignable: true)
int id = 0;
final news = ToOne<News>();
}
#JsonSerializable(explicitToJson: true)
#Entity()
class News extends Data<News> implements DataInterface<News> {
#Id(assignable: true)
#JsonKey(defaultValue: 0)
int lid = 0;
#Backlink()
final Picture = ToMany<NewsPicture>();
}

Related

How to optimize SQL query in Anylogic

I am generating Agents with parameter values coming from SQL table in Anylogic. when agent is generated at source I am doing a v look up in table and extracting corresponding values from table. For now it is working perfectly but it is slowing down the performance.
Structure of Table looks like this
I am querying the data from this table with below code
double value_1 = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.avg_value)).get(0);
double value_min = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.min_value)).get(0);
double value_max = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.max_value)).get(0);
// Fetch the cluster number from account table
int cluster_num = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.cluster)).get(0);
int act_no = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.actno)).get(0);
String pay_term = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.pay_term)).get(0);
String pay_term_prob = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.pay_term_prob)).get(0);
But this is very slow and wants to improve the performance. someone mentioned that we can create a Java class and then add the table into collection . Is there any example where I can refer. I am finding it difficult to put entire code.
I have created a class using below code:
public class Customer {
private String act_code;
private int actno;
private double avg_value;
private String pay_term;
private String pay_term_prob;
private int cluster;
private double min_value;
private double max_value;
public String getact_code() {
return act_code;
}
public void setact_code(String act_code) {
this.act_code = act_code;
}
public int getactno() {
return actno;
}
public void setactno(int actno) {
this.actno = actno;
}
public double getavg_value() {
return avg_value;
}
public void setavg_value(double avg_value) {
this.avg_value = avg_value;
}
public String getpay_term() {
return pay_term;
}
public void setpay_term(String pay_term) {
this.pay_term = pay_term;
}
public String getpay_term_prob() {
return pay_term_prob;
}
public void setpay_term_prob(String pay_term_prob) {
this.pay_term_prob = pay_term_prob;
}
public int cluster() {
return cluster;
}
public void setcluster(int cluster) {
this.cluster = cluster;
}
public double getmin_value() {
return min_value;
}
public void setmin_value(double min_value) {
this.min_value = min_value;
}
public double getmax_value() {
return max_value;
}
public void setmax_value(double max_value) {
this.max_value = max_value;
}
}
Created collection object like this:
Pls provide an reference to add this database table into collection as a next step. then I want to query the collection based on the condition
You are on the right track here!
Every time you access the database to read data there is a computational overhead. So the best option is to access the database only once, at the start of the model. Create all the objects you need, store other data you will need later into Java classes, and then use the Java classes.
My suggestion is to create a Java class for each row in your table, like you have done. And then create a map object - like you have done, but with the key as String and the value as this new object.
Then on model start you can populate this map as follows:
List<Tuple> rows = selectFrom(customer).list();
for (Tuple row : rows) {
Customer customerData = new Customer(
row.get( customer.act_code ),
row.get( customer.actno ),
row.get( customer.avg_value )
);
mapOfCustomerData.put(customerData.act_code, customerData);
}
Where mapOfCustomerData is a linkedHashMap and customer is the name of the table
See the model created in this blog post for more details and an example on using a scenario object to store all the data from the Database in a separate object
Note: The code above is just an example - read this blog post for more details on using the AnyLogic INternal Database
Before using Java classes, try this first: click the "index" tickbox for all columns that you query with a WHERE clause.

Flutter Dart | How to return different object from class Constructor

In Dart, is it possible for a constructor to cancel object creation and return a different object instead?
Use case:
Users contains a static map that maps ids to User objects.
When a User is initialized, I want the User constructor to check if User with id is already created, if so: return existing User object, else create a new User object
Example (of-course not working):
class Users {
static const Map<String, User> users = {};
}
class User {
final String id;
final String firstName;
User({required id, required firstName}) {
// If user with id already exists, return that object
if (Users.users.containsKey(id) {
return Users.users[id];
}
// Else, initialize object and save it in Users.users
this.id = id;
this.firstName = firstName;
Users.users[id] = this;
}
}
Question: IS there any way to get the above pseudo code to work?
As mentioned by jamesdlin you should use a factory constructor. Here's what is mentioned in the documentation:
Use the factory keyword when implementing a constructor that doesn’t
always create a new instance of its class.
And in your case this is exactly what you want. Now here's a code sample that does what you want:
Code sample
class Users {
// Also your Map cannot be const if you want to edit it.
static Map<String, User> users = {};
}
class User {
final String id;
final String firstName;
/// Base private constructor required to create the User object.
User._({required this.id, required this.firstName});
/// Factory used to create a new User if the id is available otherwise return the User
/// associated with the id.
factory User({required String id, required String firstName}) {
// If user with id already exists, return that object
if (Users.users.containsKey(id)) {
// Force casting as non nullable as we already checked that the key exists
return Users.users[id]!;
}
// Else, initialize object and save it in Users.users
final newUser = User._(id: id, firstName: firstName);
Users.users[id] = newUser;
return newUser;
}
}
Try the full code on DartPad
You can create a function in class to handle things you want. Here's what you can implement.
class Player {
final String name;
final String color;
Player(this.name, this.color);
Player.fromPlayer(Player another) :
color = another.color,
name = another.name;
}
If this is for caching purposes or you are not creating multiple instances of the Users class, I would suggest using a pattern where static is responsible for a list of class instances. Sometimes this helps to significantly reduce the amount of code:
class User {
static final Map<String, User> users = {};
final String id, firstName;
User._({required this.id, required this.firstName});
factory User({required String id, required String firstName}) => users[id] ??= User._(id: id, firstName: firstName);
}

storing a nested data structure in ObjectBox with type conversion method (.toObjectBox)

I guess the best (only?) way is to create a separate box for the nested structure and then to create a relation from parent (ONE) to children (MANY).
However, my issue is how to implement a .toObjectBox() method to convert my domain data model into the ObjectBox data model. Is there anything alike Either from the dartz package (returns either a left or a right side object), which transports 2 objects simultaneously?
So with the ObjectBox Entities
#Entity()
class Customer {
int id;
#Backlink('customer')
final orders = ToMany<Order>();
}
#Entity()
class Order {
int id;
final customer = ToOne<Customer>();
}
I would store the parent (customer) with the nested data objected (order) in the 2 related boxes
Customer customer = domainData.toObjectBox; // but how to get the order out?
customer.orders.add(Order('Order 1')); // shouldn't this be Order(1)?
final customerId = store.box<Customer>().put(customer);
This is how I typically implement the toObjectBox method. By here, you see that this does not work out because I would have to split parent and nested child apart. While I guess I could achieve this with some spaghetti, I wonder if there is a smart way of doing so since I guess this should be a common pattern (though I haven't found any Q&A on it).
#Entity()
Customer {
int id;
List<Order> orders;
Customer({required this.id, required this.orders});
CustomerObox toObjectBox() {
return CustomerObox(
id: id,
// orders: orders.map((x) => x.toObjectBox()).toList()
);
}
=== UPDATE =====================================
I have meanwhile tried to create a return structure myself, and now I am in the process to get this working.
class Pair<T1, T2> {
final T1 parent;
final T2 child;
Pair({required this.parent, required this.child});
}
#Entity()
class Customer {
int id;
List<Order> orders;
Customer({required this.id, required this.orders});
static Pair<CustomerObox, List<OrderObox>> toObjectBox(Customer cust) {
Pair<CustomerObox, List<OrderObox>>(
parent: CustomerObox(
id: cust.id,
),
child: cust.orders.map((o) => o.toObjectBox()).toList()
);
}
}
So I implemented toObjectBox as shown in the update of my question above.
Inside my local datasource implementation (I use the layers: presentation > business logic (bloc) > domain (models, contracts etc) > repository > datasource) I implemented the following methods
#override
Future<void> storeCustomer(Customer customer) async {
// Pair(T1 parent, T2 child) with T2 being List<OrderObox>
final Pair customerObox = Customer.toObjectBox(customer);
final CustomerObox customerParent = customerObox.parent;
final List<OrderObox> customerChildren = customerObox.child;
for (final child in customerChildren)
customerParent.records.add(child);
// puts both, parent and children:
final id = _sBox.put(customerParent);
}
#override
Future<List<Customer>> getAllCustomers() async {
final List<Customer> parents = [];
final List<CustomerObox> parentsObox = await _sBox.getAll();
for (final p in parentsObox) {
final List<CustomerObox> childrenObox = p.records;
parents.add(SessionJournalDto.fromObjectBox(p));
for (final c in childrenObox)
parents.last.records.add(Order.fromObjectBox(c));
}
return parents;
}

How can I get a default and a parameterized constructor inside the same class in Dart/Flutter?

I know that in C++ we could have both of the constructors without a problem. In Dart when I'm trying to write two constructors, it says "The default constructor is already defined"
class Human {
double height;
int age;
Human()
{
height = 0;
age = 0;
}
Human (double startingheight){ //The default constructor is already defined
height = startingheight;
}
}
Dart doesn't support methods/functions overload and will not have it in any visible future.
What you can do here is to make the parameters optional with default value:
Either as positional arguments:
class Human {
double height = 175;
Human([this.height]);
}
var human1 = Human();
var human = Human(180);
or named:
class Human {
final double height;
Human({this.height = 175});
}
var human1 = Human();
var human = Human(height: 180);
class Human{
Human(double height, int color) {
this._height = height;
this._color = color;
}
Human.fromHuman(Human another) {
this._height = another.getHeight();
this._color = another.getColor();
}
}
new Human.fromHuman(man);
This constructor can be simplified
Human(double height, int age) {
this._height = height;
this._age = age;
}
to
Human(this._height, this._age);
Named constructors can also be private by starting the name with _
Constructors with final fields initializer list are necessary:
class Human{
final double height;
final int age;
Human(this.height, this.age);
Human.fromHuman(Human another) :
height= another.height,
age= another.age;
}
Try these
//Using Default parameter values
Human({this.height = 0.0, this.age = 0});
// Named constructor
Human.startingHeight(double startingHeight){
height = startingHeight;
age = 0;//or don't use this if you want it null
}
For more info check out this page: https://dart.dev/guides/language/language-tour

Querydsl Path Depth

I have a entity Document with a list of DocumentValue
#QueryEntity
#Document
public class Document{
private List<DocumentValue> documentValues;
}
DocumentValue can has also a list of DocumentValue
#QueryEntity
public class DocumentValue {
String value;
String name;
String id;
List<DocumentValue> documentValues;
}
I am now trying to do something like
private QDocumentValue getDocumentValuePathByDepth(int depth){
ListPath path = QDocument.document.documentValues;
if (depth != null) {
for (int i = 0; i < depth; i++) {
path = path.documentValues.any();
}
}
}
Does anybody know if its possible to do an eleMatch in that depth?
Like
ListPath<QDocumentValue> query = getDocumentValuePathByDepth(5);
return query.fieldId.eq(documentFilter.getFieldId()).and(query.value.between(from, to));
One element of documentValues in that depth should fulfill both conditions
BR D.C.
elemMatch is supported in Querydsl Mongodb like this
QDocumentValue documentValue = QDocumentValue.documentValue;
query.anyEmbedded(document.documentValues, documentValue)
.on(documentValue.id.eq(documentFilter.getFieldId(),
documentValue.value.between(from, to));