In Sequelize, How do you perform a where query on an association inside of an $or statement? - associations

I have three models, User, Project and ProjectMember. Keeping things simple, the models have the following attributes:
User
- id
Project
- id
- owner_id
- is_published
ProjectMember
- user_id
- project_id
Using sequelize.js, I want to find all projects where the project owner is a specific user, or where there is a project member for that project whose user is that user, or where the project is published. I imagine the raw SQL would look something like this:
SELECT p.*
FROM Project p
LEFT OUTER JOIN ProjectMember m
ON p.id = m.project_id
WHERE m.user_id = 2
OR p.owner_id = 2
OR p.is_published = true;
There are plenty of examples out there on how to perform a query on an association, but I can find none on how to do so conditionally. I have been able to query just the association using this code:
projModel.findAll({
where: { },
include: [{
model: memberModel,
as: 'projectMembers',
where: { 'user_id': 2 }
}]
})
How do I combine this where query in an $or to check the project's owner_id and is_published columns?

It's frustrating, I worked for hours to try to solve this problem, and as soon as I ask here I found a way to do it. As it turns out, sequelize.js developers recently added the ability to use raw keys in your where query, making it (at long last) possible to query an association inside of the main where clause.
This is my solution:
projModel.findAll({
where: {
$or: {
'$projectMembers.user_id$': 2,
owner_id: 2,
is_published: true
}
},
include: [{
model: memberModel,
as: 'projectMembers'
}]
})
Note: this solution breaks if you use 'limit' in the find options. As an alternative, you can fetch all results and then manually limit them afterwards.

Related

firebase what is the best way/structure to retrieve by unique child key

I have a firebase database like this structure:
-groups
--{group1id}
---groupname: 'group1'
---grouptype: 'sometype'
---groupmembers
----{uid1}:true
----{uid2}:true
--{group2id}
---groupname: 'group2'
---grouptype: 'someothertype'
---groupmembers
----{uid1}:true
----{uid3}:true
----{uid4}:true
Now, I am trying to pull groups of authenticated user. For example for uid1, it should return me group1id and group2id, and for example uid3 it should just return group2id.
I tried to do that with this code:
database().ref('groups/').orderByChild('groupMembers/' + auth().currentUser.uid).equalTo('true').on('value' , function(snapshot) {
console.log('GROUPS SNAPSHOT >> ' + JSON.stringify(snapshot))
})
but this returns null. if I remove "equalTo" and go it returns all childs under 'groups'.
Do you know any solution or better database structure suggestion for this situation ?
Your current structure makes it easy to retrieve the users for a group. It does not however make it easy to retrieve the groups for a user.
To also allow easy reading of the groups for a user, you'll want to add an additional data structure:
userGroups: {
uid1: {
group1id: true,
group2id: true
},
uid2: {
group1id: true,
group2id: true
},
uid3: {
group2id: true
},
uid3: {
group2id: true
}
}
Now of course you'll need to update both /userGroups and /groups when you add a user to (or remove them from) a group. This is quite common when modeling data in NoSQL databases: you may have to modify your data structure for the use-cases that your app supports.
Also see:
Firebase query if child of child contains a value
NoSQL data modeling
Many to Many relationship in Firebase

Grabbing Keys with Firebase

I'm currently creating an app using ionic2 and firebase for a class and I've run into an issue.
I"m not posting code because I don't have anything relevant enough to the question.
In my database I have a 'group' that people can create. I need the 'group' to be able to store a list of users. The way I'm currently doing it is that users will click an addGroup button, and the currentUser will be added to the list of users in that group simply by typing in the name of the group. (yeah i know that's going to need a password or something similar later, people obviously shouldn't just be able to join by group name)
*Names of groups will all be different so I should be able to query the database with that and get a single reference point.
The problem is that I don't know how to properly query the database and get the key value of the group object I want.
I keep looking things up and cant find a simple way to get the key of an object. I feel like this is a dumb question but, I'm unfamiliar with typescript, firebase, and ionic2 so I'm pretty lost in everything.
Answer for the question in comment.
you can query the database like this
this.group_val:any="group1"
let group=db.list('/groups', {
query: {
orderByChild: GroupName, //here specify the name of field in groups
equalTo: this.group_val //here specify the value u want to search
}
});
group.subscribe(grp=> {
//you can use the grup object here
})
First, define your database structure. The below is probably best:
groups
<first group> (Firebase-generated ID)
name: "Cat lovers"
users:
user1ID: true
user2ID: true
<second group>
...
I assume that when the user clicks on the group he wants to join, you know the group ID. For instance, it could be the value of an option in a select.
Get the list of groups:
groups$ = this.angularFireDatabase.list('groups');
To populate a list of groups into a select:
<select>
<option *ngFor="let group of groups$ | async" [value]="group.$key">
{{group.name}}
</option>
</select>
To create a new group:
groups$.push({name: "Dog lovers"})
The select (drop-down) will update itself.
To add a user to a group:
addUserToGroup(groupId, userId) {
this.angularFireDatabase.list(`groups/${groupID}/users`).update(userID, true);
}
To find a group based on its name:
// Define the shape of a group for better type checking.
interface Group { name: string; users: {[userId: string]: boolean;}; }
// Keep local list of groups.
groups$: FirebaseListObservable<Group>;
groups: Group[];
// Keep an updated local copy of groups.
ngOnInit() {
this.angularFireDatabase.list('groups').subscribe(groups => this.groups = groups);
}
function findGroupByName(name) {
return this.groups.find(group => group.name === name);
}

DocumentDb - query on nested document and root level

Alert me | Edit | Delete | Change type
Question
You cannot vote on your own post
0
Hi!
Lets assume I have docuents in following manner:
{
id: 123,
tags: [ { name: "something" } ]
}
and I want to query all documents that contain a tag with name="searched" OR have the id=9000. I tested on playground ( https://www.documentdb.com/sql/demo )something like:
SELECT food.id, food.description, food.tags
FROM food
JOIN tag IN food.tags
WHERE food.id = "09052" or tag.name="blueberries"
but then I get a bunch of duplicate records, each document from food is times the amount of tags in that document.
How can I get distinct results when filtering on nested collection and root properties?
The ARRAY_CONTAINS built-in function might be what you need. See https://msdn.microsoft.com/library/azure/dn782250.aspx#bk_array_functions for details, i.e., something like this:
SELECT food.id, food.description, food.tags
FROM food
WHERE food.id = "09052" or ARRAY_CONTAINS(food.tags, { "name": "blueberries" })
You can test this query on the Query Playground here.
Note that the function does not use the index, so ideally you should use this when there's another filter in the query. Otherwise, the only way to do this is to use the query you had previously, then perform a "distinct" on the client side.

handling complex queries in firebase

I'm developing a shopping cart mobile application using ionic framework and FIREBASE as back-end . I have a requirement that I need to join JOIN, SORT, FILTER and PAGINATE by multiple data attributes.
Sample data structure is given below.
products : {
prod1:{
name: Samsung-s4,
type: pro,
category: Phone,
created_datetime: 1426472828282,
user: user1,
price: 400
},
prod2:{
name: iPhone 5s,
type: pro,
category: Phone,
created_datetime: 1426472635846,
user: user2,
price: 500
},
prod3:{
name: HP Laptop i3,
type: regular,
category: Computer,
created_datetime: 1426472111111,
user: user1,
price: 600
}
}
user_profiles : {
user1:{
name: abc_user,
display_name: ABC,
email: abc#mail.com
},
user2:{
name: xyz_user,
display_name: XYZ,
email: xyz#mail.com
}
}
I need to query the products using multiple ways. two simple examples given below.
1) Get all products with Pagination, where type is "pro" then join with user_profiles and SORT by created date.
2) Get all products with Pagination, filter by category then join with user_profiles and SORT by created date.
Like above there will be more and more filtering options coming in Eg: price.
My main problem is, I couldn't find straight forward way of doing this using FIREBASE query options. Also I referred firebase util documentation but there also I don't see a way of getting this done.
As far as I see only way to get this done is, do the majority of processing in client end by getting all the data (or majority of the data) in to client side and do the SORT / FILTER / PAGINATE in client end.
But we are expecting thousands of records in these schemas, therefore there will be a huge performance impact if we do the processing in client end !!
Appreciate your expertise/support to solve this problem.
Thank you in advance.
UPDATE:
I changed the data structure (as webduvet explained) and tried with firebase-util but failed to achieve what i want. CRITERIA :Products -> Filter By type/pro -> Sort By products.created_date
type: {
pro:{
product1: user1,
product3: user1,
...
},
regular:{
...
}
}
firebase-util - angularfire code
var list = $firebase(new Firebase.util.NormalizedCollection(
ref.child("products").orderByChild('created_datetime'),
ref.child('type').child('pro')
).select(
{key: "products.name" , alias: 'name'}
).ref()).$asArray();
"products" will have hundred thousand records, so we have to make sure we restrict as much possible in firebase end rather than handling in client end.
Please help !
This has to be done not by queries but by database design. You need to store data in de-normalised way for example:
type: {
pro:{
product1: user1,
product3: user1,
...
},
regular:{
...
}
}
the above structure will give you option to query all pro products and retrieve the user id as well. Firebase offer good sorting mechanism so there should not be a problem. The more complex query you require the more complex the data structure will be needed and the more denormalized data you will have.
But as pointed out by #Swordfish0321 sql type of db might suit you much better after all.

pagination in alfresco

I am working on an application which lists and searches document from alfresco. The issue is the alfresco can return upto 5000 records per query. but I don't want my application to list down all documents instead if I can some how implement pagination in alfresco, so that alfresco only return X result per page. I am using Alfresco 4 enterprise edition.
Any help or suggestion please.
UPDATE (Example)
I have written a web script which executes the query and returns all the documents satisfies the condition. Lets say, there are 5000 entries found. I want to modify my web script in a way that the web script returns 100 documents for 1st page, next 100 for second page and so on...
It'll be something like usage of Limit BY and OFFSET keywords. something like this
There are two ways to query on the SearchService (excluding the selectNodes/selectProperties calls). One way is to specify all your arguments directly to the query method. This has the advantage of being concise, but the disadvantage is that you don't get all the options.
Alternately, you can query with a SearchParameters object. This lets you do everything the simple query does, and more. Included in that more are setLimit, setSkipCount and setMaxItems, which will allow you to do your paging.
If your query used to be something like:
searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "lucene", myQuery);
You'd instead do something like:
SearchParameters sp = new SearchParameters();
sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
sp.setLanguage("lucene");
sp.setQuery(myQuery);
sp.setMaxItems(100);
sp.setSkipCount(900);
searchService.query(sp);
Assuming you have written your webscript in Javascript you can use the search.query() function and add the page property to the search definition as shown below:
var sort1 = {
column: "#{http://www.alfresco.org/model/content/1.0}modified",
ascending: false
};
var sort2 = {
column: "#{http://www.alfresco.org/model/content/1.0}created",
ascending: false
};
var paging = {
maxItems: 100,
skipCount: 0
};
var def = {
query: "cm:name:test*",
store: "workspace://SpacesStore",
language: "fts-alfresco",
sort: [sort1, sort2],
page: paging
};
var results = search.query(def);
You can find more information here: http://wiki.alfresco.com/wiki/4.0_JavaScript_API#Search_API

Resources