SendBird treats every channel as their GroupChannel. The 1:1 chat too is technically a GroupChannel with only two users (with isDistinct = true so it would return the personal chat when you attempt to create it again).
My question is, how do I search GroupChannels by their name those include group AND 1:1 chat? The group chat would have a common name that would be shown to all the users in the group. But for 1:1 chat, the GroupChannel won't have a name, and if it has, that won't be shown to the users as for 1:1 chat, we always show the other person's name (like almost all the chat systems work).
Typically the main UI list contains mixture of the group chat and 1:1 chats (all the GroupChannels).
--------------------------------
| Search Chat TextField |
|--------------------------------|
|1 John (1:1) |
|2 John's Birthday Plan (group) |
|3 Johnney Eve (1:1) |
|4 Johansson Fans (group) |
| ... |
--------------------------------
All the items are technically GroupChannel. Note that all the 1:1 chats don't have actual name as shown in the list. The name shown in the list is the other person's nickname.
Expectation:
Now, if the user searches something like "joh", then it should return all the group chats whose name contains "joh" OR all the 1:1 chats where the other person's name contains "joh". (Basically all the items shown in the above example.)
My Attempt:
My initial solution to achieve this is to keep the 1:1 channel name as <user1 nickname> & <user2 nickname>, so when the user searches for the other user by their name, the 1:1 channel would appear just like a group channel.
Example Code:
query = SBDGroupChannel.createMyGroupChannelListQuery()
query?.order = .latestLastMessage
query?.limit = 30
query?.channelNameContainsFilter = "joh"
query.loadNextPage(...)
The Problem:
The problem with this are:
If the user searches for their own name (or just the separator character & or just a whitespace), then too all the personal chat would be visible, which is irrelevant.
My system allows user to change their nickname, so every time a user changes their nickname, then all the 1:1 channel names have to be updated (which is painful).
Sunil,
Typically when you retrieve a list of group channels for a user, it retrieves all channels that the user is potentially a part of (Depending on the memberStateFilter).
If you were explicitly looking to search, rather than providing an ongoing list of channels the user is part of, you may be able to filter channels by userIds. You'd have to filter for a channel that consists of the searching user, and the desired user.
Lets look at an example, assuming your userId is John and you're looking for your chat with Jay:
let listQuery = SBDGroupChannel.createMyGroupChannelListQuery()
listQuery?.userIdsExactFilter = ["John", "Jay"]
listQuery?.loadNextPage(completionHandler: { (groupChannels, error) in
guard error == nil else {
// Handle error.
}
// Only channelA is returned in a result list through the "list" parameter of the callback method.
...
})
If you wanted to explicitly use nicknames:
let listQuery = SBDGroupChannel.createMyGroupChannelListQuery()
listQuery?.nicknameContainsFilter = ["John", "Jay"]
listQuery?.loadNextPage(completionHandler: { (groupChannels, error) in
guard error == nil else {
// Handle error.
}
// Only channelA is returned in a result list through the "list" parameter of the callback method.
...
})
You mention that you allow users to change their nicknames, and thus rooms have to be updated. It may be worth giving your group channels (even 1:1) generic names, and then dynamically generate the display name of each chat.
Since each channel returns the list of members, you could look at the array of members, filter out the user that is logged in, and then pull the nickname of the remaining user from the array. This would ensure that no matter what the user changes their nickname to, its always accurate, and you don't have to update every channel when the user updates their nickname.
************ Updated 02/10 ************
Thanks for providing an example of what you're looking to achieve. It looks like you're essentially trying to search both channelNameContainsFilter and nicknameContainsFilter using the OR operator. This is not something we (Sendbird), currently support within the iOS SDK. So the question is, what could you do to achieve this?
One option would be to utilize the Platform API to obtain this information. The List my group channels has the search_query and search_fields parameters which would allow you to utilize that OR operator to find both channel names and nicknames that match your value.
Alternatively, since the SDK does return all of the necessary data that would be required to filter for these results, you could create a front-end filter that would only display the items that match your filter results. So the SDK returns the complete channel list, you store that list, and then when the user searches, you filter through the list to find channels that match your needs and display only those.
As a side note, Stackoverflow may not be the best place for this type of discussion as there is a lot of back and forth. Please feel free to join us in our community for more support.
Related
I am building a social network that has a specialized audience.
Users are related to each other by three primary relationship types.
[:FRIENDS]->(:USER),
[:WORKS_AT]->(:COMPANY),
[:WORKED_AT]->(:COMPANY),
[:FOLLOWS].
When working through a search scenario (a user wants to find another user), I've given each relationship a "priority" (so to speak).
For example, if a user wants to find another user named "Bart Simpson" - first, we will check co-worker relationships ([:WORKS_AT],[:WORKED_AT]). I've assigned those relationships a priority of 1. That way, "Bart Simpson" who works with me will appear in the search results before "Bart Simpson" - who lives hundreds of miles away in Springfield.
The second priority is [:FRIENDS]->(:USER). Do any of my friends have a friend named "Bart Simpson?" Priority #2.
The last priority is a global search. I don't have any co-workers named "Bart Simpson", my friends don't have any friends named "Bart Simpson" - but I met Bart at a conference, and I want to "friend" him. So, I've added a "Global" search. Find any users named "Bart Simpson".
So far, this is my Cypher:
optional match (u:USER {id:'1'})-[:WORKS_AT|:WORKED_AT]-(w:COMPANY)-[r]-(f:USER)
with collect(f{.*, priority:1,relationship:r.title,type:type(r)}) as user
optional match (u:USER {id: '1'})-[:FRIENDS]-(:USER)-[r:FRIENDS]-(f:USER)
with user + collect(f{.*, priority:2,relationship:r.title,type:type(r)}) as user
optional match (f:USER)
where f.id <> '1'
with user + collect(f{.*, priority:3,relationship:'',type:''}) as user
unwind user as users
with users as user
where toLower(user.last_name) STARTS WITH toLower('Sc') OR toLower(user.first_name) STARTS WITH toLower('Sc')
return distinct user
This is fantastic - however, a user could work at the same company, as well as
be friends, as well as appear in the global search. So - we have the potential for three (or more) "copies" of the same user - with different relationship attributes. The relationship attributes are important because in the app, they provide important context to the search. "Bart Simpson - Works at XYZ Company."
So what I'm really looking for is the ability to either return the user record with the highest priority - and do that based on the "ID" field. If that doesn't work, I could see a situation where we try to update the property of a node. So, when the query hits the priority 2 search, if there is already a user in the collection with the same "ID", it just appends the P2 relationship type to the record. Either is fine with me.
I'm open to suggestions and listening!
So, I've made some progress!
MATCH
(subject:USER {id:'1'})
MATCH
(subject)-[:WORKS_AT|:WORKED_AT]-(w:COMPANY)-[r]-(f1:USER)
WHERE
toLower(f1.last_name) STARTS WITH toLower('Sc') or
toLower(f1.first_name) STARTS WITH toLower('Sc')
WITH
COLLECT(f1.id) AS userIds,
COLLECT(f1{.*,priority:1,rType:type(r), title:r.title, detail:w.name}) AS users
OPTIONAL MATCH
(subject)-[:FRIEND]-(fw:USER)-[r:FRIEND]-(f2:USER)
WHERE
NOT(f2.id in userIds) AND
(
toLower(f2.last_name) STARTS WITH toLower('Sc') or
toLower(f2.first_name) STARTS WITH toLower('Sc')
)
WITH
users + COLLECT(f2{.*,priority:2,rType:"FRIEND", title:"Friends with " + fw.first_name + " " + fw.last_name, detail:''}) AS users,
userIds + collect(f2.id) AS userIds
OPTIONAL MATCH
(f3:USER)
WHERE
NOT(f3.id in userIds) AND
(
toLower(f3.last_name) starts with toLower('Sc') OR
toLower(f3.first_name) starts with toLower('Sc')
)
WITH
users + COLLECT(f3{.*,priority:3,rType:"GLOBAL", title:"", detail:''}) AS users
RETURN
users
The query has evolved a bit. Essentially, at the first stage, we collect the userIds of the items that were returned. At each subsequent stage, the results returned are compared against the running list of ids. If the id of the result is already in the list of ids, it is filtered out - thus ensuring a unique id in the set.
This is working - and for now, I'm going to run with it. Is this the most efficient query, or is there a better way to deal with this scenario?
Good morning,
I want to build a structure in Neo4J where I can handle my users and groups (kind of ACL). The idea is to have for each user and for each group a node with all the details. The groups shall become a graph where a root group will have sub-groups that can have also sub-groups without limit. The relation will be -[:IS_SUBGROUP_OF]- - so far nothing exciting. Every user will be related to a group with -[:IS_MEMBER_OF]- to have a clear assignment. Of course a user can be a member of 1 or more groups. Some users will have a different relation like -[:IS_LEADER_OF]- to identify teamlead of the groups.
My tasks:
Assignment: I can query each member of a group with a simple query, I can even query members of the subgroups using the current logged in and asking user:
MATCH (d1:Group:Local) -- (c:User)
MATCH (d:User) -[:IS_MEMBER_OF|IS_LEADER_OF]- (g:Group:Local)-[:IS_SUBGROUP_OF*0..]->(d1)
WHERE c.login = userLogin
RETURN DISTINCT d.lastname, d.firstname
I get every related user to every group of the current user and below (subgroups). Maybe you have a hint how I cna improve the query or the model.
Approval
Here I am stucked as I want to have all users of the current group from the querying user and all members of all subgroups - except the leader of the current group. The reason behind is that a teamlead shall not be able to approve actions for himself but though for every other member of his group and all members of subgroups including their teamleads.
I tried to use the relations -[:IS_LEADER_OF]- to exclude them but than I loose also the teamleads of the subgroups. Does anyone has an idea how I would either change the model or how I can query the graph to get all users except the teamlead of the current group?
Thanks for your time,
Balael
* EDIT *
I think I am getting close, I just need to understand the results of those both queries:
MATCH (d:User) -- (g:Group) WHERE g.uuid = "xx"
RETURN d.lastname, d.firstname
Returns all user in this group no matter what relationship (leader / member)
MATCH (d:User) -- (g:Group), (g)--(c:User{uuid:"yy"})
RETURN d.lastname, d.firstname
Returns all user of that group except the user c. I would have expected to get c as well in the list with d-users as c is part of that group and should be found with (d:User).
I do not understand the difference between both queries, maybe someone has a hint for me?
You can simplify your query slightly (however this should not have an impact on performance):
MATCH (d:User) -[:IS_MEMBER_OF|IS_LEADER_OF]- (g:Group:Local)-[:IS_SUBGROUP_OF*0..]->(d1:Group:Local)--(c:User{login:"userlogin"})
RETURN DISTINCT d.lastname, d.firstname
Don't completely understand your question, but I assume you want to make sure that d1 and c are not connected by a IS_LEADER_OF relationship. If so, try:
MATCH (d:User) -[:IS_MEMBER_OF|IS_LEADER_OF]- (g:Group:Local)-[:IS_SUBGROUP_OF*0..]->(d1:Group:Local)-[r]-(c:User{login:"userlogin"})
WHERE type(r)<>'IS_LEADER_OF'
RETURN DISTINCT d.lastname, d.firstname
following up on * EDIT * in the question
In a MATCH you specify a path. By definition a path does not use the same relationship twice. Otherwise there is a danger to run into infinite recursion. Looking at the second query in the "EDIT" section above: the right part matches yy's relationship to the group whereas the left part matches all user related to this group. To prevent multiple usage of the same relationship the left part does not hit use yy
I want to filter my application users based on their phone number or email, but I don't want exact match, instead part of the email or part of the number users should return in response. Is there a way in Quickblox iOS SDK?
Suppose, I've some quickblox users like below :
ID NAME Email Address Mobile Number
User1 | yuyuqabc#somedomain.com | +91-12345-67890
User2 | qerqrorp#somedomain.com | +1-123-000-7891
User3 | xyzabcqry#somedomain.com | +64-123-456-78
Now the filter should apply like this,
if I want to query on email, which contains "abc" then should return 1st and 3rd user.
if I want to query on phone number, which contains "23" then should return all users.
if I want to query on phone number, which contains "234" then should return 1st and 3rd user.
Is it possible?
It is possible only if you will use CustomObjects module instead of Users.
So you will need to create User class in CustomObjects and you will have all operators working there.
In Users it is impossible.
my relationships look like this
A-[:CHATS_WITH]->B - denotes that the user have sent at least 1 mesg to the other user
then messages
A-[:FROM]->message-[:SENT_TO]->B
and vice versa
B-[:FROM]->message-[:SENT_TO]->A
and so on
now i would like to select all users a given user chats with together with the latest message between the two.
for now i have managed to get all messages between two users with this query
MATCH (me:user)-[:CHATS_WITH]->(other:user) WHERE me.nick = 'bazo'
WITH me, other
MATCH me-[:FROM|:SENT_TO]-(m:message)-[:FROM|:SENT_TO]-other
RETURN other,m ORDER BY m.timestamp DESC
how can I return just the latest message for each conversation?
Taking what you already have do you just want to tag LIMIT 1 to the end of the query?
The preferential way in a graph store is to manually manage a linked list to model the interaction stream in which case you'd just select the head or tail of the list. This is because you are playing to the graphs strengths (traversal) rather than reading data out of every Message node.
EDIT - Last message to each distinct contact.
I think you'll have to collect all the messages into an ordered collection and then return the head, but this sounds like it get get very slow if you have many friends/messages.
MATCH (me:user)-[:CHATS_WITH]->(other:user) WHERE me.nick = 'bazo'
WITH me, other
MATCH me-[:FROM|:SENT_TO]-(m:message)-[:FROM|:SENT_TO]-other
WITH other, m
ORDER BY m.timestamp DESC
RETURN other, HEAD(COLLECT(m))
See: Neo Linked Lists and Neo Modelling a Newsfeed.
I want to use javascript to fetch data with yql from flickr,
e.g.
select id from flickr.photos.search(10) where text = 'music' and license=4
however, I would like to fetch 10 random rows, rather then the latest, since the latest tend to be 10 photos all from the same person.
ist that possible in yql itself (I suspect not),
or any workarounds that could bring the same effect?
(it does not have to be complete random, the main thing I want to avoid is to get 10 photos from the same poster)
To get only results from unique owners, you can use the unique() function (docs).
My suggestion would be to query for a larger result set (more likely to have 10 unique people) then call unique() followed by truncate() to limit to 10 results, as below.
select id from flickr.photos.search(100) where text = 'music' and
license=4 | unique(field="owner") | truncate(count=10)