how to get each object from a many-to-many relationship - google-cloud-datastore

Person is an object has name and favoriteFoods,
Food is an object has name and foodFans.
the relationship between Person and Food is many to many
A person can have lots of favoriteFoods,
and the food can have lots of foodFans
i wrote a simple java to get the each favoriteFoods from one person
i want to know what i have done is correct?
Is there a better solution?
THANKS
PersistenceManager pm = PMF.get().getPersistenceManager();
//select from ...
Query query = pm.newQuery(Person.class);
//where ...
query.setFilter("name == nameParam");
//order by ...
query.setOrdering("id desc");
//declare a parameter to use later
query.declareParameters("String nameParam");
try {
#SuppressWarnings("unchecked")
//execute query
List<Person> results = (List<Person>) query.execute(person_name);
//if found
if (results.iterator().hasNext()) {
for (Person p : results) {
out.println("<p>" + p.getKey() + "</p>");
out.println("<p>" + p.getName() + "</p>");
Set<Key> foods = p.getFavoriteFoods();
Iterator<Key> i = foods.iterator();
while(i.hasNext()) {
Food f = pm.getObjectById(Food.class, i.next());
out.println("<p>" + f.getName()+ "</p>");
i.remove();
}
}
} else {
out.println("<p>Not Found.</p>");
}
} finally {
query.closeAll();
}

looks like you're using JDO. the app engine JDO docs use this exact example - people and favorite foods - and they describe one recommended way to implement it: unowned many-to-many relationship.
specifically, you'd first determine which side will generally have fewer related entities. in this case, the average Food will probably have more fans (millions) than the average Person will have favorite foods (tens to hundreds).
put a key list property in Person, e.g. Set<Key> favoriteFoods, for their favorite foods. to get a person's favorite foods, you'd just load the Person entity (which costs one datastore get) and look at the favoriteFood set. to get a food's fans, you'd query for the Person entities whose favoriteFood property contains (= in GQL) that food.

Related

How to make a search function able to search two different type of data/item in one search bar

I have code to search the merchants, but I need it to be able to search using the category/subcategories
I have here is:
Category > as the (Food, Leisure, Services, etc.)
so I'll take Food Category for example
Subcategory > Desserts , Bakery, Coffee, Chinese Cuisine
and under each subcategories was the merchants
Sample:
Category(Food) > Subcat(Coffee) > Merchant(J.Co)
so I would just like to type Coffee and hit enter, and the list of merchants under Subcategory of Coffee should show up...
Any tips and tricks?
Thanks by the way.
var merchantList = [];
function PopulateMerchant() {
merchantList = [];
$.ajax({
type: "GET",
url: "/Merchant/ActiveMerchants",
cache: false,
success: function (data) {
if (data != null) {
for (var a = 0; a <= data.length - 1; a++) {
var name = data[a]["name"];
var merId = data[a]["merchantId"];
var type = {
id: merId,
label: name
};
merchantList.push(type);
}
}
},
error: function (error) {
console.log(error);
}
});
return merchantList;
}
That is going to depend on how you are organising your data on the back end and how you organise your search query on your data.
I'll assume you are using a stored procedure in a SQL database, however the same basis process would probably be workable on other data sources. Obviously I've made some assumptions about how your data is connected, but you can adjust to suit
This works based on having your search term to be used in a LIKE statement, so when you set the #searchTerm parameter you would set it to "%{searchterm}%" (with the % symbols)
CREATE PROCEDURE dbo.DoMerchantSearch
(
#searchTerm nvarchar(50)
)
AS
BEGIN
Select merchant.*
From merchant
inner join subCategory on subcategory.id = merchant.subcategoryId
inner join category on category.id = subcategory.categoryId
Where merchant.enabled = 1
and (merchant.name like #searchTerm
or subCategory.name like #searchTerm
or category.name like #searchTerm)
END
So if your search term was "Coffee", this would create an input parameter of "%Coffee%" and would match any category, sub-category or merchant that had "Coffee" in their name.
Hope that points you in the right direction.
Edit: Did forget to mention that you'd also need to ensure your javascript AJAX call was also passing the search term.

flutter query multiple collections in firestore

I am playing with flutter but I ran into an issue with firestore that I can't figure out.
let's say I would like to retrieve the purchaser history of a customer and I have a firestore as described below so I have a collection of "user" within that contains a document of user_id's and then within that, I have a "products" collection with a document that contains the product_id's my aim is to gather the product id's for the selected user and then retrieve the products price from the products collection?
users
user_id_1
products
purchace_id
product_id
quantity
products
product_id
product_desc
price
the issue I am having is while I can get the id for the products from the user table but I can't then see a simple way of how I can use this data like with SQL where we would make a simple join on the product_id to get the price where the id's match?
any help would be much appreciated.
Thanks for replying Frank van Puffelen 2 seprate querys seams reasonable but it brings up other issues with the second query as now with the code below i can get the documents_id from the user and then do a query on the products and everything looks ok so i think i am going in the correct direction as i can print the id of the document in the loop to ensure i am accessing the correct ones but when i change this to get a snapshot of the document i cant access the data within this is probably me doing something silly or misunderstanding something but this feels very awkward compared to sql when trying to get and work with the data. For example if i return the data it wants it to be in the form of a future> however once i return the data in that format then i can no longer access the id's in the same way as i was doing.
Future<List<DocumentSnapshot>> getSeedID() async{
var data = await Firestore.instance.collection('users').document(widget.userId).collection('products').getDocuments();
var productList = data.documents;
print("Data length: ${productList.length}");
for(int i = 0; i < productList.length; i++){
var productId = Firestore.instance.collection('products').document(productList[i]['productId']).documentID;
if(productId != null) {
print("Data: " + productId);
}
}
return productList;
}
Short answer i didn't pay attention to how you use a future
Get the productId from the user table
Future<List<DocumentSnapshot>> getProduceID() async{
var data = await Firestore.instance.collection('users').document(widget.userId).collection('Products').getDocuments();
var productId = data.documents;
return productId;
}
Then call this using .then() rather than try to apply the returned data to the variable as that is not how a future it works
var products;
getProduceID().then((data){
for(int i = 0; i < s.length; i++) {
products = Firestore.instance.collection('products')
.document(data[i]['productID'])
.snapshots();
if (products != null) {
products.forEach((product) {
print(product.data.values);
});
}
}
});

Adaptive User Management

I have built a review app based on Google's "people viewer" template that allows managers to create and edit reviews for their direct reports.
The app contains the directory model as well as three roles: Admins, HR, EndUsers.
The app contains a user settings model that allows to create and store user settings similar to the "people skills" template.
The app contains a review model that will contain the reviews for every employee. As one employee can have several reviews, this will be a one-to-many relation, either linked to directory model or user settings model.
The reviews should be readable by managers chain of manager. For this I have created a server script, assuming that the EmployeeEmail will be additionally stored in the review. But maybe there is a better alternative?
function getDirectReportsChainForUser_(query) {
var userQuery = app.models.Directory.newQuery();
userQuery.filters.PrimaryEmail._equals = query.parameters.PrimaryEmail;
userQuery.prefetch.DirectReports._add();
userQuery.prefetch.DirectReports.DirectReports._add();
var users = userQuery.run();
if (users.length === 0) {
return [];
}
var user = users[0];
var directs = user.DirectReports;
var records = [];
for (var i = 0; i <= directs.length; i++) {
records.push(directs[i].PrimaryEmail);
}
// The following lines are based on the asumption that the EmployeeEmail
// will be stored in the review in case that there is no better alternative.
//The question that then remains is how to recursively add the DirectReports
//of the DirectReports to the array???
var reviewQuery = app.models.Reviews.newQuery();
reviewQuery.filters.EmployeeEmail._in = records;
return reviewQuery.run();
}
The manager should be able to define whether one or more of his deputies can read the reviews for his unit, too. My idea was to solve this issue through a many-to-many relation between the directory and review model, but I am not sure how to implement it?
Furthermore, once a manager or his deputy departures, it should be possible for the Admin to dissolve the connection and to reconnect the reviews to a successor. Therefore I was thinking about integrating a multiselect in the admin page. Would this be feasible?
Here I see at least two distinct questions:
is there better way to associate directory model's record and ordinary data model than just adding primary email field to the data model
Nope, at this time it is not possible to establish relations between data (SQL/Drive Tables) and directory models.
how to recursively get all direct reports for a user
App Maker's Directory Model is a wrapper on top of G Suit Admin SDK's Directory API that exposes just a small subset of its powerful features. When you add Directory Model App Maker automatically plugs in correspondent Apps Script advance service:
Since we already have configured Directory API we can unleash its full power and easily fetch all manger's subordinates with a single call (or multiple if you have a need to support paging). In order to do that we will use Users.List API method with managerId query parameter (the only one that allows us to query all subordinates down the tree). Here are reference for the minimal set of search query parameters quoted from the full search documentation (without those parameters query would not work or wouldn't work in a way we need):
managerId: The ID of a user's manager either directly or up the management chain.
domain: The domain name. Use this field to get fields from only one domain. To return all domains for a customer account, use the customer query parameter instead. Either the customer or the domain parameter must be provided.
viewType: Whether to fetch the administrator-only or domain-wide public view of the user. For more information, see Retrieve a user as a non-administrator (admin_view is default value so we need to override it with domain_view).
query: Query string for searching user fields. For more information on constructing user queries, see Search for Users.
/**
* Fetches all reviews associated with all subordinate employees (both direct
* and indirect reports).
*/
function getAllReportsEmails(managerId) {
var emails = [];
var result = AdminDirectory.Users.list({
domain: 'ENTER HERE YOUR DOMAIN (exapmle.com)',
query: 'managerId=' + managerId,
viewType: 'domain_public',
maxResults: 100
});
if (result.users) {
emails = result.users.map(function (user) {
return user.primaryEmail;
});
}
return emails;
}
/**
* Fetches all reviews associated with all subordinate employees (both direct
* and indirect reports).
*/
function getAllReportsReviewsForManager_(query) {
var userQuery = app.models.Directory.newQuery();
// For better security I would recommend to use
// Session.getActiveUser().getEmail() instead of parameter
// passed from the client.
userQuery.filters.PrimaryEmail._equals = Session.getActiveUser().getEmail();
var users = userQuery.run();
if (users.length === 0) {
return [];
}
var manager = users[0];
var managerId = manager._key;
var allReportsEmails = getAllReportsEmails(managerId);
var reviewQuery = app.models.Reviews.newQuery();
reviewQuery.filters.EmployeeEmail._in = allReportsEmails;
return reviewQuery.run();
}
Pavel, I tried to integrate the ideas you gave me into one server script that returns an array of the manager and his whole subordinate chains (direct reports + indirect reports), so that I can use it whenever needed. I turned into a recursive function to get the direct reports and indirect reports on the next lower level. Is there a way to get the whole chain?
function getSubordinatesChainForUser(query) {
var userQuery = app.models.Directory.newQuery();
userQuery.filters.PrimaryEmail._equals = Session.getActiveUser().getEmail();
userQuery.prefetch.DirectReports._add();
userQuery.prefetch.DirectReports.DirectReports._add();
var users = userQuery.run();
if (users.length === 0) {
return [];
}
var userEmails = users.map(function(manager){
var employeeEmails = manager.DirectReports.map(function(employee){
return employee.PrimaryEmail;
});
return manager.PrimaryEmail + ',' + employeeEmails;
});
return userEmails;
}

Entity Framework: Mapping Existing Tables w/ no Referential Integrity

In this scenario, I have an existing database with three models:
Address, which has a 'AddressId', 'ParentId', 'City', 'State', etc
Person, which has a 'PersonId', 'Name', etc.
Company, which has a 'CompanyId', 'Name', etc.
Because Address can contain rows for more models than just one table, there's no foreign key; the Address column 'ParentId' would contain either a 'PersonId' or 'CompanyId'.
To generate my DbContext, I used Entity Framework Power Tools Beta 2. Due to the lack of referential integrity, the classes it generated for Person and Company do not contain a collection of addresses like they should.
How would I go about altering those classes to add in this "Addresses" property, and ensure it's mapped to the Address table correctly?
At the moment I'm doing something like this; I'd rather have the context map it if possible:
public IEnumerable<Address> Addresses
{
get
{
IEnumerable<Address> addresses = null;
using(MyDb db = new MyDb())
{
addresses = db.Addresses.Where(a => a.ParentId == this.PersonId)
}
return addresses;
}
}
Thanks!
If you can, you should add an unique id to the Address table and then store that id with the entity that it belongs to, whether it be a person, company, vendor etc.
If you can't because of the multiple address scenario, then you could create a new table AddressXRef table that stores the GUID's of the entities (vendor, person, company etc), and the GUID of the address; so every entity could have multiple address and EF will be quite happy with this setup as there will be keys all around.
(I'd also want some sort of address type indicator on the Xref table so I knew what kind of address it was, mailing, shipping, etc)
Create partial classes for both Company and Person which have that property you have created in it.
public partial class Company
{
public IEnumerable<Address> Addresses { ... }
}

Different RavenDB collections with documents of same type

In RavenDB I can store objects of type Products and Categories and they will automatically be located in different collections. This is fine.
But what if I have 2 logically completely different types of products but they use the same class? Or instead of 2 I could have a generic number of different types of products. Would it then be possible to tell Raven to split the product documents up in collections, lets say based on a string property available on the Product class?
Thankyou in advance.
EDIT:
I Have created and registered the following StoreListener that changes the collection for the documents to be stored on runtime. This results in the documents correctly being stored in different collections and thus making a nice, logically grouping of the documents.
public class DynamicCollectionDefinerStoreListener : IDocumentStoreListener
{
public bool BeforeStore(string key, object entityInstance, RavenJObject metadata)
{
var entity = entityInstance as EntityData;
if(entity == null)
throw new Exception("Cannot handle object of type " + EntityInstance.GetType());
metadata["Raven-Entity-Name"] = RavenJToken.FromObject(entity.TypeId);
return true;
}
public void AfterStore(string key, object entityInstance, RavenJObject metadata)
{
}
}
However, it seems I have to adjust my queries too in order to be able to get the objects back. My typical query of mine used to look like this:
session => session.Query<EntityData>().Where(e => e.TypeId == typeId)
With the 'typeId' being the name of the new raven collections (and the name of the entity type saved as a seperate field on the EntityData-object too).
How would I go about quering back my objects? I can't find the spot where I can define my collection at runtime prioring to executing my query.
Do I have to execute some raw lucene queries? Or can I maybe implement a query listener?
EDIT:
I found a way of storing, querying and deleting objects using dynamically defined collections, but I'm not sure this is the right way to do it:
Document store listener:
(I use the class defined above)
Method resolving index names:
private string GetIndexName(string typeId)
{
return "dynamic/" + typeId;
}
Store/Query/Delete:
// Storing
session.Store(entity);
// Query
var someResults = session.Query<EntityData>(GetIndexName(entity.TypeId)).Where(e => e.EntityId == entity.EntityId)
var someMoreResults = session.Advanced.LuceneQuery<EntityData>(GetIndexName(entityTypeId)).Where("TypeId:Colors AND Range.Basic.ColorCode:Yellow)
// Deleting
var loadedEntity = session.Query<EntityData>(GetIndexName(entity.TypeId)).Where(e =>
e.EntityId == entity.EntityId).SingleOrDefault();
if (loadedEntity != null)
{
session.Delete<EntityData>(loadedEntity);
}
I have the feeling its getting a little dirty, but is this the way to store/query/delete when specifying the collection names runtime? Or do I trap myself this way?
Stephan,
You can provide the logic for deciding on the collection name using:
store.Conventions.FindTypeTagName
This is handled statically, using the generic type.
If you want to make that decision at runtime, you can provide it using a DocumentStoreListner

Resources