I want to make MongoEngine Query that just return the vehicle that i want, but it doesnt work - mongodb

I'm using MongoEngine to create a query that return just vehicles that match with the constraint. As can you see Vehicle is an EmbeddedDocument and Owner is the parent document. My query is using Q, the result of the query is different that just vehicles matched with Honda brand and Element model.
from mongoengine.queryset.visitor import Q
def get_vehicles():
vehicles = json.loads(Owner.objects(Q(vehicle__brand='Honda') and Q(vehicle__model='Element')).only('vehicle').to_json())
return {"vehicles":vehicles}
class Vehicle(engine.EmbeddedDocument):
model = engine.StringField(required=True, max_length=50)
brand = engine.StringField(required=True, max_length=100)
plates = engine.StringField(required=True, max_length=50)
year = engine.IntField(required=True)
color = engine.StringField(required=True, max_length=80)
type = engine.StringField(required=True, max_length=80)
subtype = engine.StringField(required=True, max_length=80)
doors = engine.IntField(required=True)
transmission = engine.StringField(required=True, max_length=80)
photos = engine.ListField(engine.StringField(max_length=150))
equipment = engine.ListField(engine.StringField(max_length=80))
powered_by = engine.StringField(required=True, max_length=80)
availability = engine.ListField(engine.DateField())
pickup_place = engine.PointField()
creation_date = engine.DateTimeField(required=True)
class Owner(engine.Document):
_id = engine.ObjectIdField()
name = engine.StringField(required=True, max_length=100)
surname = engine.StringField(required=True, max_length=100)
gender = engine.StringField(required=True, max_length=1)
birthday = engine.DateField(required=True)
creation_date = engine.DateTimeField(required=True)
last_update = engine.DateTimeField()
photo = engine.StringField(required=True, max_length=200)
access = engine.EmbeddedDocumentField(Access)
address = engine.EmbeddedDocumentField(Address)
vehicle = engine.EmbeddedDocumentListField(Vehicle)

i'm near to the result that i want, but i need to add the result the owner id to vehicle.
def get_vehicles():
matched_owners = json.loads(Owner.objects(Q(vehicle__brand='VW') and Q(vehicle__model='Jetta')).only('vehicle').to_json())
vehicles = [ vehicle for owner in matched_owners for vehicle in owner['vehicle'] if vehicle['brand'] == "VW" and vehicle['model'] == "Jetta"]
return {"vehicles":vehicles}
The query on Mongo shell that match with the result that i want is:
db.owner.find({"vehicle.brand":"VW"},{vehicle:{$elemMatch:{model:"Jetta"}}});

I get the result that i want on MongoShell
db.owner.aggregate([{ $unwind:"$vehicle"}, {$match:{ "vehicle.model":"Jetta", "vehicle.brand":"VW"}}, {$project: {"vehicle.model":1, "vehicle.brand":1, "vehicle.color":1, "vehicle.doors":1, "vehicle.transmission":1, "vehicle.powered_by":1 ,"vehicle.photos":1, "vehicle.equipment":1, "vehicle.availability":1, "vehicle.pickup_place":1}} ]);```
i attempted to do the same on MongoEngine, but the result is different:
```python
def get_vehicles():
pipeline = [
{ "$unwind":"$vehicle"},
{
"$match":{ "vehicle.model":"Jetta", "vehicle.brand":"VW"}
},
{
"$project": {"vehicle.model":1, "vehicle.brand":1, "vehicle.color":1, "vehicle.doors":1, "vehicle.transmission":1, "vehicle.powered_by":1 ,"vehicle.photos":1, "vehicle.equipment":1, "vehicle.availability":1, "vehicle.pickup_place":1}
}
]
vehicles = Owner.objects.aggregate(pipeline)
return {"vehicles":vehicles}```

Indeed you are querying the parent collection (Owner) so the query returns all owner document with any of the Vehicle matching your Q conditions.
Although not elegant, adding a second filtering pass in Python will do the trick:
from mongoengine import *
connect()
class Vehicle(EmbeddedDocument):
model = StringField(required=True, max_length=50)
brand = StringField(required=True, max_length=100)
def __repr__(self):
return f'Vehicle({self.brand} - {self.model})'
class Owner(Document):
vehicles = EmbeddedDocumentListField(Vehicle)
Owner(vehicles=[Vehicle(model="Civic", brand="Honda"), Vehicle(model="Element", brand="Honda")]).save()
Owner(vehicles=[Vehicle(model="Golf", brand="VW")]).save()
def get_vehicles(brand, model):
matched_owners = Owner.objects(Q(vehicles__brand=brand) and Q(vehicles__model=model)).only('vehicles')
vehicles = [vehicle for owner in matched_owners for vehicle in owner.vehicles if vehicle.brand == brand and vehicle.model == model]
return vehicles
get_vehicles("Honda", "Element") # Print [Vehicle(Honda - Element)]
What you want is probably feasible using an aggregation pipeline but if your use case is really this one, you'll be just fine with this
Off topic but I'd recommend you to avoid defining a '_id' attribute, MongoEngine gives you a id = ObjectIdField() field by default and uses "_id" attribute internally so it's not a good idea to overwrite it (main MongoEngine contributor here)

Related

Is my Linq Query Optimal and High performance in EF Core 3.1?

can i remove vg.First().Voucher ? and replace the beter code? what is the optimal and best practice?
is convertable this code to another method? like chain method?
var query = from v in _journalLineRepository.TableNoTracking
.Include(j => j.Voucher).AsEnumerable()
group v by v.AccountId into vg
select new // <-- temporary projection with group by fields needed
{
AccountId = vg.Key,
Credit = vg.Sum(v => v.Credit),
Debit = vg.Sum(v => v.Debit),
Voucher = vg.First().Voucher
} into vg
join p in _partyRepository.TableNoTracking.Include(p => p.PartyPhones).AsEnumerable() on vg.AccountId equals p.AccountId // <-- additional join(s)
select new PartyDeptorAndCreditorViewModel
{
PartyId = p.Id,
FullName = p.FullName,
PhoneNo = p.PartyPhones.FirstOrDefault(p => p.IsActive)?.Phone,
ProjectId = vg.Voucher.ProjectId,
AccountId = vg.AccountId.Value,
Creditor = vg.Credit,
Deptor = vg.Debit,
Balance = vg.Credit - vg.Debit,
VoucherDate = vg.Voucher.VoucherDate,
VoucherRegisterDate = vg.Voucher.VoucherDate,
BalanceType =
vg.Debit > vg.Credit ? AccountingComplexEnum.ShowPartyBalanceParamSearch.Deptor.ToDisplay(DisplayProperty.Name) :
vg.Debit < vg.Credit ? AccountingComplexEnum.ShowPartyBalanceParamSearch.Creditor.ToDisplay(DisplayProperty.Name) :
AccountingComplexEnum.ShowPartyBalanceParamSearch.ZeroBalance.ToDisplay(DisplayProperty.Name),
};
I'd certainly be looking at the SQL query generated. At face value I see a few warning flags that it may not be composing a query but possibly pre-executing to in-memory processing which would be inefficient. It would firstly depend on what these .TableNoTracking methods/properties return, and the use of .AsEnumerable on the eager load joins.
Firstly, when projecting with Select, eager load joins (.Include) are not necessary. The projections will take care of the joins for you, provided it is projecting down to SQL. If you take out the .Include().AsEnumerable() calls and your query still works then it is likely projecting down to SQL. If it is no longer working then it's processing in memory and not efficiently.
Edit: Nope, the inner projection won't resolve: Regarding the .Voucher, your final projection is using 2 fields from this entity, so it stands you could replace this in the initial projection:
select new // <-- temporary projection with group by fields needed
{
AccountId = vg.Key,
Credit = vg.Sum(v => v.Credit),
Debit = vg.Sum(v => v.Debit),
Voucher = vg.Select(v => new { v.ProjectId, v.VoucherDate }).First()
} into vg
When it comes to transformations like this:
BalanceType = vg.Debit > vg.Credit
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Deptor.ToDisplay(DisplayProperty.Name)
: vg.Debit < vg.Credit
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Creditor.ToDisplay(DisplayProperty.Name)
: AccountingComplexEnum.ShowPartyBalanceParamSearch.ZeroBalance.ToDisplay(DisplayProperty.Name),
... inside a projection, this sends off warning flags as Linq2EF needs to compose projections down to SQL so methods/extensions like ToDisplay won't be understood. Instead, since this is based solely on the Credit/Debit amounts, I'd move this to be computed by the property in the view model:
select new PartyDeptorAndCreditorViewModel
{
PartyId = p.Id,
FullName = p.FullName,
PhoneNo = p.PartyPhones
.Where(p => p.IsActive)
.Select(p => p.Phone)
.FirstOrDefault(),
ProjectId = vg.Voucher.ProjectId,
AccountId = vg.AccountId.Value,
Creditor = vg.Credit,
Deptor = vg.Debit,
Balance = vg.Credit - vg.Debit,
VoucherDate = vg.Voucher.VoucherDate,
VoucherRegisterDate = vg.Voucher.VoucherDate
};
Then in the view model:
[Serializable]
public class PartyDebtorAndCreditorViewModel
{
// ...
public decimal Balance { get; set; }
public string BalanceType
{
get
{
return Balance < 0
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Deptor.ToDisplay(DisplayProperty.Name)
: Balance > 0
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Creditor.ToDisplay(DisplayProperty.Name)
: AccountingComplexEnum.ShowPartyBalanceParamSearch.ZeroBalance.ToDisplay(DisplayProperty.Name);
}
}
}

Flask Translate Choice in Form To ForeignKey Pointing to Another Table

This part of the app works, buts it's ugly and not sustainable. Need a more evolved solution.
PROBLEM I AM TRYING TO SOLVE:
This part of the application enables users to access a form to enter purchases they've made and store them in a Postgres DB. I am using Flask SQLAlchemy ORM.
Within my purchase table exists a field store_id, that has a ForeignKey relationship to my store table. I don't want my user to select a store ID # in the form, so I am using a SelectField to enable them to choose the store name instead. However, I can't seem to find a sustainable way to translate the store name back to its associated ID. Right now I am using the ugly IF statements seen below.
What is a better way to map/translate store name to ID which is already housed in the "store" table in my DB?
MY MODEL:
class Purchase(db.Model):
id = db.Column(db.Integer, primary_key=True)
item = db.Column(db.String(80))
quantity = db.Column(db.Integer)
unit_cost = db.Column(db.Integer)
total_cost= db.Column(db.Integer)
date = db.Column(db.DateTime)
store_id = db.Column(db.Integer, db.ForeignKey('store.id'))
MY FORM:
class CreatePurchase(FlaskForm):
item = StringField('Item', validators=[DataRequired()])
quantity = IntegerField('Quantity', validators=[DataRequired()])
unit_cost = IntegerField('Unit Cost', validators=[DataRequired()])
total_cost = IntegerField('Total Cost', validators=[DataRequired()])
store_id = SelectField("Store Selector", choices=[('0','Select Store'),('1','Furgesons'), ('2','Ocean State'), ('3','Chewy'), ('4','Amazon'), ('5', 'Rumford')])
date = DateField('Purchase Date', validators=[DataRequired()])
submit = SubmitField('Enter')
MY ROUTE:
#main.route('/enter_purchases', methods=['GET', 'POST'])
def enter_purchase():
form = CreatePurchase()
x = str(form.store_id) # this is a store name from our form
p = 0
if "Ocean State" in x:
p = 2
elif "Amazon" in x:
p = 4
elif "Furgesons" in x:
p = 1
elif "Chewy" in x:
p = 3
elif "Rumford" in x:
p = 5
if form.validate_on_submit():
purchase = Purchase(item=form.item.data, quantity=form.quantity.data, unit_cost=form.unit_cost.data,
total_cost=form.total_cost.data, date=form.date.data,store_id=p)
db.session.add(purchase)
db.session.commit()
flash('New purchase added successfully')
return redirect(url_for('main.success'))
return render_template('enter_purchases.html', form=form)
You have a store table, with a numeric id (as the PK) and a name attribute:
class Store(db.Model):
store_id = ..
store_name = ..
You populate your form with all of the unique values from the store_name. This needs to be a dynamically generated form, so instead of using a form that is statically created do something like:
def CreatePurchase()
class TempForm(FlaskForm):
item = StringField('Item', validators=[DataRequired()])
quantity = IntegerField('Quantity', validators=[DataRequired()])
unit_cost = IntegerField('Unit Cost', validators=[DataRequired()])
total_cost = IntegerField('Total Cost', validators=[DataRequired()])
date = DateField('Purchase Date', validators=[DataRequired()])
submit = SubmitField('Enter')
choices = ## some SQLalchemy code to get all unique store_names from the table
TempForm.store_id = SelectField("Store Selector", choices=choices)
return TempForm()
Then form.store_id will provide the right id, but display the string name of the store in the form.
The key in this setup is making sure you use the right SQLalchemy code to populate the SelectField dynamically. Basically for each store in the table you can just iterate through something like this:
choices = list()
for store in Store.query.all(): # <- assumes store_id and store_names are unique
choices.append((store.store_id, store.store_name))

How to filter categories from this model in views.py

In this function I'm not able to filter Categories alone. So please tell me how to do that? Here I used array model field for categories as well as specifications. From this array model field I could not get the categories column using query
set category_name=Product.objects.values_list('categories', flat=True).distinct()
And in that filter should be distinct values. So what query has to given for retrieving this categories values
models.py
class Product(models.Model):
name = models.CharField(max_length=255)
image = models.ImageField(upload_to = '',blank="True")
categories = models.ArrayModelField(
model_container=Categories,
model_form_class=CategoriesForm
)
specifications = models.ArrayModelField(
model_container=Specifications,
model_form_class=SpecificationsForm
)
description = models.TextField()
reviews = models.ArrayModelField(
model_container=Reviews,
model_form_class=ReviewsForm
)
drizzly = models.BooleanField()
complete = models.BooleanField()
def __str__(self):
return self.name
class Categories(models.Model):
name = models.CharField(max_length=255)
class Meta:
abstract = True
views.py
def index(request):
data = Product.objects.all()
category_name=Product.objects.values_list('categories', flat=True).distinct()
return render(request, 'db.html', data':data,'category_name':category_name})

How can I join tables with different data types using Entity Framework

With the situation of using Entity Framework,I want to join tables with two field in same value,but the fields are identified with different data types. For example,there is a field which the data type is 'Guid',but in another table the associated field is 'string' type. I am confused to achieve the goal. Can somebody give me some ideas.Here are my codes, emps.EmpGuid is a 'Guid' data type and flowCode.Object is a string:
DbSet<tb_Emp_Manager_Zbdl> dbEmp = zbdlcontext.Emp_Manager;
DbSet<Tb_Corp_CompanyInfo_Zbdl> dbCompany = zbdlcontext.Corp_CompanyInfos;
DbSet<Tb_FlowCode_Zbdl> dbFlowCode = zbdlcontext.FlowCodes;
DbSet<Tb_Emp_Post_Zbdl> dbEmpPost = zbdlcontext.Emp_Post;
var query = from emps in dbEmp
join companies in dbCompany on emps.CorpGuid equals companies.CorpUserGuid
join flowCode in dbFlowCode on new { EmpGuid = emps.EmpGuid.ToString(), AreaCode = areaCode } equals new { EmpGuid = flowCode.ObjectId, AreaCode = flowCode.AreaCode } into jFlowCodes
from flowCodes in jFlowCodes.DefaultIfEmpty()
join post in dbEmpPost on emps.EmpGuid equals post.EmpGuid into jPosts
from posts in jPosts.DefaultIfEmpty()
select new tb_Emp_Manager()
{
EmpGuid = emps.EmpGuid,
AreaCode = emps.AreaCode,
FlowCode = flowCodes.FlowCode,
corpName = companies.CorpName
};

Entity Framework - Select * from Entities where Id = (select max(Id) from Entities)

I have an entity set called Entities which has a field Name and a field Version. I wish to return the object having the highest version for the selected Name.
SQL wise I'd go
Select *
from table
where name = 'name' and version = (select max(version)
from table
where name = 'name')
Or something similar. Not sure how to achieve that with EF. I'm trying to use CreateQuery<> with a textual representation of the query if that helps.
Thanks
EDIT:
Here's a working version using two queries. Not what I want, seems very inefficient.
var container = new TheModelContainer();
var query = container.CreateQuery<SimpleEntity>(
"SELECT VALUE i FROM SimpleEntities AS i WHERE i.Name = 'Test' ORDER BY i.Version desc");
var entity = query.Execute(MergeOption.OverwriteChanges).FirstOrDefault();
query =
container.CreateQuery<SimpleEntity>(
"SELECT VALUE i FROM SimpleEntities AS i WHERE i.Name = 'Test' AND i.Version =" + entity.Version);
var entity2 = query.Execute(MergeOption.OverwriteChanges);
Console.WriteLine(entity2.GetType().ToString());
Can you try something like this?
using(var container = new TheModelContainer())
{
string maxEntityName = container.Entities.Max(e => e.Name);
Entity maxEntity = container.Entities
.Where(e => e.Name == maxEntityName)
.FirstOrDefault();
}
That would select the maximum value for Name from the Entities set first, and then grab the entity from the entity set that matches that name.
I think from a simplicity point of view, this should be same result but faster as does not require two round trips through EF to sql server, you always want to execute query as few times as possible for latency, as the Id field is primary key and indexed, should be performant
using(var db = new DataContext())
{
var maxEntity = db.Entities.OrderByDecending(x=>x.Id).FirstOrDefault()
}
Should be equivalent of sql query
SELECT TOP 1 * FROM Entities Order By id desc
so to include search term
string predicate = "name";
using(var db = new DataContext())
{
var maxEntity = db.Entities
.Where(x=>x.Name == predicate)
.OrderByDecending(x=>x.Id)
.FirstOrDefault()
}
I think something like this..?
var maxVersion = (from t in table
where t.name == "name"
orderby t.version descending
select t.version).FirstOrDefault();
var star = from t in table
where t.name == "name" &&
t.version == maxVersion
select t;
Or, as one statement:
var star = from t in table
let maxVersion = (
from v in table
where v.name == "name"
orderby v.version descending
select v.version).FirstOrDefault()
where t.name == "name" && t.version == maxVersion
select t;
this is the easiest way to get max
using (MyDBEntities db = new MyDBEntities())
{
var maxReservationID = _db .LD_Customer.Select(r => r.CustomerID).Max();
}