I am using Golang (GORM) + Postgres. I am trying to model a business situation where a seller sells things to buyers, each creating an order transaction.
I have an Order Gorm model, as well as a Buyer and a Seller Gorm model. The Buyer and the Seller are already rows created in the database.
One buyer HasMany orders.
One seller HasMany orders.
To map out this relation, I believe I just create the respective Buyer/Seller struct (standard Gorm models), and then make an Order struct like so:
type Order struct {
ID int64 `json:"ID"gorm:"primary_key;auto_increment:true"`
Buyer *Buyer `json:"Buyer"`
Seller *Seller `json:"Seller"`
// ... other data ...
}
I'm assuming the above automatically creates the relationship, but I am not entirely sure. I can create an Order with this function, and this returns fine:
func CreateOrder(buyer *entity.Buyer, seller *entity.Seller) (*entity.Order, error) {
order := &entity.Order{
User: buyer,
Sitter: seller,
// ... other data ...
}
db.Table("orders").Create(order)
return order
}
If I go to Postgres CLI, the TABLE orders; does not show the columns buyer or seller. I would expect a column of their IDs. So this is why I am unsure this is working. This could definitely be a problem in itself.
Anyways, what I really want to do is be able to check if any orders currently exist for a Buyer / Seller. But I don't really see anyway to do that with gorm queries. I would imagine in SQL it would be something like:
func FindOrder(buyer *entity.Buyer, seller *entity.Seller) {
db.Raw(`GET order FROM orders WHERE buyer = ?, seller = ?`, buyer, seller)
// OR this ???
db.Table("orders").First(buyer, buyer).First(seller, seller)
}
But I don't know of any Gorm helper function that actually does this. I also want this to be efficient because buyer and seller each have their ID primary keys.
How can I find an Order based on the Buyer / Seller like in the example above?
As an alternative, I am thinking of adding (buyer ID + seller ID) to make a custom order ID primary_key. This seems hacky though, as I feel like the whole point of relations is so I don't have to do something like this.
If you need to see the seller id and the buyer id in the orders table, include two fields for that in your orders struct, also you can use the foreignKey tag to populate those fields (by default they are populated with the primary id of the associated table record, you can use references tag as mentioned here to change that).
type Order struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement:true"`
BuyerID int64 `json:"buyer_id" gorm:"index"`
SellerID int64 `json:"seller_id" gorm:"index"`
Buyer *Buyer `json:"buyer" gorm:"foreignKey:BuyerID"`
Seller *Seller `json:"seller" gorm:"foreignKey:SellerID"`
}
type Buyer struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement:true"`
Name string `json:"name"`
}
type Seller struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement:true"`
Name string `json:"name"`
}
As for the function to find orders given the buyer AND the seller you can use something like,
func findOrders(db *gorm.DB, buyerID int,sellerID int)[]Order{
orders := make([]Order,0)
db.Where("buyer_id=? AND seller_id=?",buyerID,sellerID).Find(&Order{}).Scan(&orders)
return orders
}
in contrast if you need to find orders for a given buyer OR the seller,
func findOrder(db *gorm.DB, buyerID int,sellerID int)[]Order{
orders := make([]Order,0)
db.Where("buyer_id=? OR seller_id=?",buyerID,sellerID).Find(&Order{}).Distinct().Scan(&orders)
return orders
}
The index tag covers the indexing requirement for orders table.
Use simple raw query builder for large queries
https://perfilovstanislav.github.io/go-raw-postgresql-builder/#simple-example
Related
Actually I'm confused for the case, which relation fits best for my case, and in my opinion the best one is to have a table with 3 primary keys.
To be more specific.
I have a Person model in one of my db's, which has structure like
Person:
Id,
FirstName,
LastName,
...
And the other model Department, which has structure mentioned below
Department:
Id,
Name,
Description,
...
And goal is to set up Editors of schedule for each department and add also admins, whioch will approve requested schedules from editors. Editors and Admins are from same Person table, and if to assume, we need to map some Persons and department with some type.
I'm thinking about to have a mapping table with structure
PersonID,
DepartmentID,
Type (editor or admin)
And not sure, which relation fits best for this. If to have belongsToMany relation here with primary keys PersonID and DepartmentID, we will face an issue, because same Person possibly can be as editor and as admin for one single department. I have MS SQL server as a db.
Any suggestions will be appreciated
you can define many to many relations and use wherePivot method to select by pivot table Type column:
// Department model
public function admins()
{
return $this->belongsToMany(Person::class)->wherePivot('type', 'admin');
}
public function editors()
{
return $this->belongsToMany(Person::class)->wherePivot('type', 'editor');
}
// Person model
public function departmentsWhereIsAdmin()
{
return $this->belongsToMany(Department::class)->wherePivot('type', 'admin');
}
public function departmentsWhereIsEditor()
{
return $this->belongsToMany(Department::class)->wherePivot('type', 'editor');
}
// Note: we use methods names without parentheses
// usage for department
$department = Department::first(); // for example
dump($department->admins);
dump($department->editors);
// usage for person
$person = Person::first(); // for example
dump($person->departmentsWhereIsAdmin);
dump($person->departmentsWhereIsEditor);
we have a problem to query our database in a meant-to-be fashion:
Tables:
employees <1-n> employee_card_validity <n-1> card <1-n> stamptimes
id id id id
employee_id no card_id
card_id timestamp
valid_from
valid_to
Employee is mapped onto Card via the EmployeeCardValidity Pivot which has additional attributes.
We reuse cards which means that a card has multiple entries in the pivot table. Which card is right is determined by valid_from/valid_to. These attributes are constrained not to overlap. Like that there's always a unique relationship from employee to stamptimes where an Employee can have multiple cards and a card can belong to multiple Employees over time.
Where we fail is to define a custom relationship from Employee to Stamptimes which regards which Stamptimes belong to an Employee. That means when I fetch a Stamptime its timestamp is distinctly assigned to a Card because it's inside its valid_from and valid_to.
But I cannot define an appropriate relation that gives me all Stamptimes for a given Employee. The only thing I have so far is to define a static field in Employee and use that to limit the relationship to only fetch Stamptimes of the given time.
public static $date = '';
public function cardsX() {
return $this->belongsToMany('App\Models\Tempos\Card', 'employee_card_validity',
'employee_id', 'card_id')
->wherePivot('valid_from', '>', self::$date);
}
Then I would say in the Controller:
\App\Models\Tempos\Employee::$date = '2020-01-20 00:00:00';
$ags = DepartmentGroup::with(['departments.employees.cardsX.stamptimes'])
But I cannot do that dynamically depending on the actual query result as you could with sql:
SELECT ecv.card_id, employee_id, valid_from, valid_to, s.timestamp
FROM staff.employee_card_validity ecv
join staff.stamptimes s on s.card_id = ecv.card_id
and s.stamptimes between valid_from and coalesce(valid_to , 'infinity'::timestamp)
where employee_id = ?
So my question is: is that database desing unusual or is an ORM mapper just not capable of describing such relationships. Do I have to fall back to QueryBuilder/SQL in such cases?
Do you suit your database model towards ORM or the other way?
You can try:
DB::query()->selectRaw('*')->from('employee_card_validity')
->join('stamptimes', function($join) {
return $join->on('employee_card_validity.card_id', '=', 'stamptimes.card_id')
->whereRaw('stamptimes.timestamp between employee_card_validity.valid_from and employee_card_validity.valid_to');
})->where('employee_id', ?)->get();
If your Laravel is x > 5.5, you can initiate Model extends the Pivot class I believe, so:
EmployeeCardValidity::join('stamptimes', function($join) {
return $join->on('employee_card_validity.card_id', '=', 'stamptimes.card_id')
->whereRaw('stamptimes.timestamp between employee_card_validity.valid_from and employee_card_validity.valid_to');
})->where('employee_id', ?)->get();
But code above is only translating your sql query, I believe I can write better if I know exactly your use cases.
I have a scenario where multiple loopings are causing the system resource error.
I need some help with map of map syntax or coding sample for this requirement.
Requirement is:
Account has 1 or more ReportCard records.
ReportCard has Account and Contact.
Now i need to get the list of ReportCards and filter by 1 per contact and recently created records only.
If ReportCard has 2 records with same contact, include only recently created.
// get list of unique accounts from the set
list<Account> accList = new list<Account >([SELECT Id,Average_of_Pulse_Check_Recommend_Score_N__c,Average_of_Recommend_Score_Lanyon_N__c,Average_of_Touchpoint_Recommend_Score_N__c,Average_of_Touch_Point_Satisfaction_N__c FROM Account WHERE Id in:AccIds]);
list<ReportCard__c> allRCList = new list<ReportCard__c>([SELECT Id,Net_Promoter_text__c,CreatedDate, Contact__c, Account__c, RecordTypeID, Touchpoint_Satisfaction_text__c FROM ReportCard__c WHERE Account__c in:accList Order By Account__c, CreatedDate Desc]);
List<ReportCard__c> rcListbyAccounts = new List<ReportCard__c>();
for(Account acc:accList)
{
Any help would be appreciated.
Thanks
I'm not sure I understand your situation correctly. You've skipped the for loop - I strongly suspect any issues you have there sit in the loop rather than in the queries.
Looks like you should read about using relationship queries (salesforce versions of JOIN in regular database): http://www.salesforce.com/us/developer/docs/soql_sosl/Content/sforce_api_calls_soql_relationships.htm
Pay special attention to subqueries (which behave similar to how a related list behaves on record's detail page).
From what I see I'd say you don't need to query for Accounts at all, or at least not like that. This will work equally well:
SELECT Id,Net_Promoter_text__c,CreatedDate, Contact__c, Account__c, ...
FROM ReportCard__c
WHERE Account__c in:accIds
ORDER BY Account__c, CreatedDate Desc
Now lets attack this:
List of ReportCards and filter by 1 per contact and recently created
records only. If ReportCard has 2 records with same contact, include
only recently created.
I'd reverse it - I'd start the query from Contact level, go down to the related list of Report Cards and pick the latest one. That way it eliminates the issue with duplicate contacts for you. Something like this:
SELECT Id, Name, Email,
(SELECT Id, Net_Promoter_text__c, CreatedDate, Account__c, Account__r.Name, Account__r.Average_of_Pulse_Check_Recommend_Score_N__c
FROM ReportCards__r
WHERE Account__c IN :accIds
ORDER BY CreatedDate DESC
LIMIT 1)
FROM Contact
WHERE AccountId IN :accIds
This goes from Contact "down" to Report Cards (via the relationship name ReportCards__r) and then "up" from Card to Account via Account__r.Name, Account__r.Average_of_Pulse_Check_Recommend_Score_N__c...
Hey Salesforce experts,
I have a question on query account information efficiently. I would like to query accounts based on the updates in an activityHistory object. The problem I'm getting is that all the accounts are being retrieved no matter if there's "complete" activeHistory or not. So, Is there a way I can write this query to retrieve only accounts with activeHistory that has status="complete" and Type_for_reporting='QRC'?
List<Account> AccountsWithActivityHistories = [
SELECT
Id
,Name
,( SELECT
ActivityDate
,ActivityType
,Type_for_Reporting__c
,Description
,CreatedBy.Name
,Status
,WhatId
FROM ActivityHistories
WHERE Status ='complete' and Type_for_Reporting__c = 'QRC'
)
FROM Account
];
You have a WHERE clause on the histories but you still miss one on the Account level.
For example this would return only Accounts that have Contacts:
SELECT Id, Name
FROM Account
WHERE Id IN (SELECT AccountId FROM Contact) // try with NOT IN too
With Activities it's trickier because they don't like to be used in WHERE in that way.
http://www.salesforce.com/us/developer/docs/officetoolkit/Content/sforce_api_calls_soql_select.htm
The following objects are not currently supported in subqueries:
ActivityHistory
Attachments
Event
EventAttendee
Note
OpenActivity
Tags (AccountTag, ContactTag, and all other tag objects)
Task
Additionally the fine print at the bottom of ActivityHistory definition is also a bit discouraging.
The following restrictions on users who don’t have “View All Data” permission help prevent performance issues:
In the main clause of the relationship query, you can reference only
one record. For example, you can’t filter on all records where the
account name starts with ‘A’; instead, you must reference a single
account record.
You can’t use WHERE clauses.
You must specify a limit of 499 or fewer on the number of rows returned in the list.
You must sort on ActivityDate in ascending order and LastModifiedDate in descending order; you can display nulls last. For
example: ORDER BY ActivityDate ASC NULLS LAST, LastModifiedDate DESC.
Looks like you will need multiple queries. Go for Task (or Event, depending for which the custom field is visible), compose a set of AccountIds and then query the Accounts?
Or you can manually filter through list from your original query, copying accounts to helper list:
List<Account> finalResults = new List<Account>();
for(Account a : [SELECT...]){
if(!a.ActivityHistories.isEmpty()){
finalResults.add(a);
}
}
Ok, I am total newbie so bear with me.
Trying to implement an ordering system and wish
to save the orders to the database with LINQ to Entities. I can do it now
but for each new object that is saved to the orders table
a new row is inserted, with new OrderNo for each ProductID where as I obviously
should be able to have multiple ProductID's for each OrderNo.
Everything is very simplified as I am just testing.
I have an orders table with columns as such:
OrderNo PK, Identity specification
Line int PL
ProductID int
and a products table
ProductID int PK
An order entity object is instantiated and its properties
are populated with data from a form which is posted to an action method.
It is then saved to the orders table with the following code:
(DropDownList1Value) has value of an existing ProductID and "DropDownList1Value" is the id of the DropDownList element in view.
[HttpPost]
public ActionResult OrderProcessor()
{
int productId = int.Parse(Request.Form["DropDownList1Value"]);
var order = new Order();
order.ProductID = productId;
context.Orders.AddObject(order);
context.SaveChanges();
return View(order);
}
So the records that are inserted look as such:
Sorry, couldn't line up the values under their respective column name in this editor.
OrderNo Line ProductID
101 0 3
102 0 5
103 0 2
Where as I want something like this:
OrderNo Line ProductID
101 1 3
101 2 5
101 3 2
102 1 2
So I wish to know how can I modify the orders table so it
can have multiple records with same "OrderNo" and just increment for "Line" for diff ProductID's and how do I go about inserting such records with LinQ to Entities where
I will obviously have many ProductId from multiple DropDowLists
and they will all be for the one order.
Currently I have foreign key dependency on ProductID in Products table,
so no record in the Orders table can have ProductID which does not exists in the Products table.
I need to make the table depend on the whole key that is OrderNo + Line
and have the "Line" auto increment.
Or is there a much better way of implementing of what I am after here?
Thanks.
Let me first explain briefly what I understood.
There is an invoice, which contains several products for one order number.
and this is how your invoice looks like:
Order Number: 101
------------------
Sl. Products
1 3
2 5
3 2
Before answering I want to point out that you are taking OrderId from a form (That is from client side) This is a wrong and INSECURE approach. Let the order id be AutoGenerated by database.
I would suggest to tweak your database design a little.
Here is a solution that will work.
Note: I am consedering your database support Auto-Increment, for MS SQL replace it with IDENTITY, for Oracle you need to create a sequence.
Product (
id INT PK AUTO-INCREMENT
);
Order (
id INT PK AUTO-INCREMENT
user-id INT FK # user who purchased
### and other common details Like date of purchase etc.
);
Order-Detail (
id INT PK AUTO-INCREMENT
order-id INT FK # Common order id
pdt-id INT FK # product which was purchased.
);
When you make a purchase:
1. Insert a row in order table
2. Fetch the last inserted id
3. Insert order-id from last step and products which are purchased in Order-Detail table,
Fetch all the orders made by a user:
1. Read from order table.
Fetch all products purchased for an order:
1. Fetch details from Order-Detail
Note: You will get List of products purchased, Use Order-detail.id as "Line"
EDIT:
Thanx to HLGEM's comment
If you think price of a product may change then instead of updating the price add a new row to the table (and flag the old table so that it wont be visible, you can also have a column in new table pointing to old table), thus old purchase will point to old product and new orders will point to updated (new) row.
There is one more approach this problem:
store the current cost of product in order-detail table.
If you are facing difficulty understanding above solution here is another and simpler one.
In Order table, Make a composite primary key including OrderNo and Line.
Whenever inserting into database you will need to generate line number in your code, which you can do by runnign a loop over array of propduct being purchased.
I think it would be better to split your current Order table into two separate tables:
Order table
(PK, Identity specification) OrderId
Perhaps other fields like Invoice address, Delivery address, etc.
OrderLine table
(PK, Identity specification) OrderLineId
(FK to Order table) OrderId
(FK to Product table) ProductId
For both tables you have an Entity in your class model: class Order and class OrderLine and a one-to-many relationship between them, so Order has a collection of OrderLines.
Creating an order with all order lines would then look like this:
var order = new Order();
foreach (var item in collection)
{
var orderLine = new OrderLine()
// Get productId from your DropDownLists
orderLine.ProductId = productId;
order.OrderLines.Add(orderLine);
}
context.Orders.AddObject(order);
context.SaveChanges();
Edit
The MVC MusicStore Tutorial might also help for the first steps to create an order processing system with ASP.NET MVC and Entity Framework. It contains classes for orders and order details (among others) and explains their relationships.