How to search in Quickblox Arrays - quickblox

I am using http://quickblox.com/developers/SimpleSample-customObjects-android#Search_operators to find keywords in "Array" data type.
But it searches by first string in array only. How to search among all strings in array?
public static void getNews(final Context context, final Handler handler, final int page) {
ArrayList<String> catSearch = new ArrayList<>();
ArrayList<NewsCategory> categories = Cache.getNewsCategories(context);
for(NewsCategory category : categories) {
if(Cache.isCategoryChecked(context, category.getId())) {
catSearch.add(category.getId());
}
}
ArrayList<String> tagSearch = new ArrayList<>();
ArrayList<NewsTag> tags = Cache.getNewsTags(context, Cache.SELECTED_TAGS);
for(NewsTag tag : tags) {
tagSearch.add(tag.getTitle());
}
QBRequestGetBuilder builder = new QBRequestGetBuilder();
if(tagSearch.size() == 0) {
builder.ctn("LangId", Cache.getNewsLanguage(context)).
in("CategoryId", catSearch.toArray());
} else {
builder.ctn("LangId", Cache.getNewsLanguage(context)).in("Tags", tagSearch.toArray()).
in("CategoryId", catSearch.toArray());
}
QBCustomObjects.getObjects("News", builder,
new QBEntityCallbackImpl<ArrayList<QBCustomObject>>() {
#Override
public void onSuccess(ArrayList<QBCustomObject> customObjects, Bundle params) {
ArrayList<String> images = new ArrayList<>();
ArrayList<News> newsArray = new ArrayList<>();
for (QBCustomObject object : customObjects) {
try {
HashMap<String, Object> pairs = object.getFields();
String id = object.getCustomObjectId();
long created = object.getCreatedAt().getTime();
News news = new News(pairs, id, created);
newsArray.add(news);
Log.e("", "Tags: " + pairs.get("Tags"));
if (!news.getPhoto().equals("")) {
images.add(news.getPhoto());
}
} catch (Exception e) {
e.printStackTrace();
}
}
Cache.saveNews(context, newsArray);
if (handler != null) {
handler.sendEmptyMessage(6);
}
}
#Override
public void onError(List<String> errors) {
Helper.showErrorToast(context, errors);
if (handler != null) {
handler.sendEmptyMessage(1);
}
}
});
}
UPD:
We held some research and here is the results:
1. When we search for tag "Русский":
QBRequestGetBuilder builder = new QBRequestGetBuilder();
builder.in("Tags", "Русский");
https://api.quickblox.com/data/News.json?Tags[in]=%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9
RESULTS:
news id: 55b4a74f535c12b641004f3e; tags: [Русский, советы]
news id: 55b91b78535c129a1d0039f2; tags: [Русский, Россия]
news id: 55ba9482535c1238ce003522; tags: [Русский, Россия]
news id: 55ba96c4535c1298ec0003fc; tags: [Русский, советы]
news id: 55ba973f535c1298ec0004d7; tags: [Русский]
news id: 55ba9967535c12e2c00017f9; tags: [Русский, Россия]
news id: 55ba99e3535c1298ec00099f; tags: [Русский, Россия]
news id: 55bd592c535c12a8d6000327; tags: [Русский]
news id: 55bb32ae535c12e543006e15; tags: [Русский, религия, пятница, Коран]
So, it was not searched for tag "Русский" when it is not a first tag in array. We have checked databse - we have some news with Tags ["Россия","Русский"]
2. When we search for tags "Русский", "Томск":
QBRequestGetBuilder builder = new QBRequestGetBuilder();
builder.in("Tags", "Русский", "Томск");
https://api.quickblox.com/data/News.json?Tags[in]=%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9%2C%D0%A2%D0%BE%D0%BC%D1%81%D0%BA
3. When we search for tags "Томск", "Русский":
QBRequestGetBuilder builder = new QBRequestGetBuilder();
builder.in("Tags", "Томск", "Русский");
https://api.quickblox.com/data/News.json?Tags[in]=%D0%A2%D0%BE%D0%BC%D1%81%D0%BA%2C%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9
RESULTS FOR 2 AND 3 THE SAME NOW:
news id: 555c46736390d8a0ca03ab61; tags: [Томск, Алмере]
news id: 555c46b26390d8a7ce01439e; tags: [Томск, Алмере, Нидерланды, Россия]
news id: 555c50bf6390d8b66300dadf; tags: [Россия, Томская область, Томск]
news id: 555d7f5c6390d8eb8504df15; tags: [Россия, Томская область, Томск, Russia, Moscow, Москва]
news id: 556454536390d894de005c22; tags: [Томск, Россия]
news id: 55a905ba535c1207e600251d; tags: [Томск, Россия]
news id: 55b4a74f535c12b641004f3e; tags: [Русский, советы]
news id: 55b91b78535c129a1d0039f2; tags: [Русский, Россия]
news id: 55ba9482535c1238ce003522; tags: [Русский, Россия]
news id: 55ba96c4535c1298ec0003fc; tags: [Русский, советы]
news id: 55ba973f535c1298ec0004d7; tags: [Русский]
news id: 55ba9967535c12e2c00017f9; tags: [Русский, Россия]
news id: 55ba99e3535c1298ec00099f; tags: [Русский, Россия]
news id: 55bd592c535c12a8d6000327; tags: [Русский]
news id: 55bb32ae535c12e543006e15; tags: [Русский, религия, пятница, Коран]
news id: 55666ac66390d8ce7900044f; tags: [Томск, Россия]

According to that doc http://quickblox.com/developers/SimpleSample-customObjects-android#Search_operators
with arrays you can use only in, nin and all operators
UPD
For example, I have a class Post with a field tags. And a have a record where tags=["man","golf","car"]
This is my requests:
http://api.quickblox.com/data/Post.json?tags[in]=car&token=70f997fecf68b25f8992271b5f943503884f3f52
http://api.quickblox.com/data/Post.json?tags[in]=man&token=70f997fecf68b25f8992271b5f943503884f3f52
http://api.quickblox.com/data/Post.json?tags[in]=golf&token=70f997fecf68b25f8992271b5f943503884f3f52
They all return the same record:
{"class_name":"Post","skip":0,"limit":0,
"items":[{"_id":"55ba0597535c12a4120011e8","_parent_id":"",
"body":"How are you?",
"created_at":1438254487,"tags":["man","golf","car"],
"title":"best post","updated_at":1438254487,"user_id":291}]}

Related

Vue Apollo "__typename" is undefined in updateQuery

I'm attempting to create a "Show More" button for my posts index. The index query loads fine with the first 5 posts, when I click the Show More button I can see new posts being returned, however I receive a bunch of errors like:
Missing field id in {
"__typename": "Post",
"posts": [
{
"id": "5f2b26600c3ec47b279d8988",
"title":
I receive one of each of these errors pretty much for each post attribute (id, title, content, slug, etc). This prevents the actual new posts from being added to the index. What causes this issue?
<script>
import postsQuery from '~/apollo/queries/blog/posts';
const pageSize = 5;
export default {
name: 'BlogIndex',
data: () => ({
loadingMorePosts: false,
page: 0,
pageSize,
}),
apollo: {
postsCount: {
prefetch: true,
query: postsQuery,
variables: {
page: 0,
pageSize,
}
},
posts: {
prefetch: true,
query: postsQuery,
variables: {
page: 0,
pageSize,
}
},
},
computed: {
morePosts() {
return this.posts.length < this.postsCount.aggregate.totalCount;
}
},
methods: {
async fetchMorePosts() {
this.page += this.pageSize;
this.$apollo.queries.posts.fetchMore({
variables: {
page: this.page,
pageSize,
},
updateQuery: (previousResult, { fetchMoreResult }) => {
const newPosts = fetchMoreResult.posts;
console.log('typename: ', previousResult.posts.__typename); <--- returns undefined
if (!newPosts.length) return previousResult;
return {
posts: {
__typename: previousResult.posts.__typename,
posts: [...previousResult.posts, ...newPosts],
}
}
}
})
},
},
}
</script>
UPDATE: added imported posts query
query Posts($page: Int!, $pageSize: Int!) {
posts(
start: $page
limit: $pageSize
sort: "published_at:desc"
where: { published: true }
) {
id
title
content
slug
published
createdAt
updatedAt
published_at
}
postsCount: postsConnection(where: { published: true }) {
aggregate {
totalCount
}
}
}
I think the problem is here:
return {
posts: {
__typename: previousResult.posts.__typename,
posts: [...previousResult.posts, ...newPosts],
}
}
I'm pretty sure __typename is supposed to belong to each post object, not part of the collection of posts. Let me know how if something like this fixes it:
return {
posts: {
posts: [...previousResult.posts, ...newPosts]
}
}
and changing the query to:
query Posts($page: Int!, $pageSize: Int!) {
posts(
start: $page
limit: $pageSize
sort: "published_at:desc"
where: { published: true }
) {
__typename // add this here
id
title
content
slug
published
createdAt
updatedAt
published_at
}
postsCount: postsConnection(where: { published: true }) {
aggregate {
totalCount
}
}
}

Cannot get new array to save

I am working on getting a blog set up and I am having trouble getting my comments data associated with the individual blog post. I am using Mongoose and Express. I am trying to create a new posts and push it to the blog array, but the association is not saving. I can see both the comment and blog objects in my DB individually when I pull up the Mongo shell, but they are not associated in the DB. However, they are showing correctly in the console in the my route.
Here are my two relevant schemas that are up in different files.
Comment schema:
var mongoose = require("mongoose");
var commentSchema = new mongoose.Schema({
author: String,
desc: String,
posted: {type: Date, default: Date.now()}
});
module.exports = mongoose.model("Comment", commentSchema);
Blog schema:
var mongoose = require("mongoose");
var blogSchema = new mongoose.Schema({
author: String,
title: String,
desc: String,
posted: {type: Date, default: Date.now()},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
module.exports = mongoose.model("Blog", blogSchema);
Route:
app.post("/blogs/:id/comments", function(req, res){
Blog.findById(req.params.id, function(err, blog){
if(err){
// console.log(err);
res.redirect("/blogs");
} else {
Comment.create(req.body.comment, function(err, comments){
if(err){
console.log(err);
} else {
blog.comments.push(comments);
blog.save();
console.log(blog);
res.redirect("/blogs/" + blog._id);
}
});
}
});
});
console results from above:
{ _id: 5a3ef348fcdd624a0c8416fb,
title: 'Ah, a new post!',
author: 'Lefty',
desc: 'Here we are trying to see about fixing the issue with comments not being associated to the blog posts, but still being created.',
__v: 0,
comments:
[ { _id: 5a3f06c0f33db14984baca92,
desc: 'Save the turtles.',
author: 'April',
__v: 0,
posted: 2017-12-24T01:45:16.864Z } ],
posted: 2017-12-24T00:21:34.514Z }
Here is the information in my Mongo shell:
> db.blogs.find()
{ "_id" : ObjectId("5a3ef348fcdd624a0c8416fb"), "title" : "Ah, a new post!", "author" : "Lefty", "desc" : "Here we are trying to see about fixing the issue with comments not being associated to the blog posts, but still being created.", "comments" : [ ], "posted" : ISODate("2017-12-24T00:21:34.514Z"), "__v" : 0 }

GraphQL queries with tables join using Node.js

I am learning GraphQL so I built a little project. Let's say I have 2 models, User and Comment.
const Comment = Model.define('Comment', {
content: {
type: DataType.TEXT,
allowNull: false,
validate: {
notEmpty: true,
},
},
});
const User = Model.define('User', {
name: {
type: DataType.STRING,
allowNull: false,
validate: {
notEmpty: true,
},
},
phone: DataType.STRING,
picture: DataType.STRING,
});
The relations are one-to-many, where a user can have many comments.
I have built the schema like this:
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: {
type: GraphQLString
},
name: {
type: GraphQLString
},
phone: {
type: GraphQLString
},
comments: {
type: new GraphQLList(CommentType),
resolve: user => user.getComments()
}
})
});
And the query:
const user = {
type: UserType,
args: {
id: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(_, {id}) => User.findById(id)
};
Executing the query for a user and his comments is done with 1 request, like so:
{
User(id:"1"){
Comments{
content
}
}
}
As I understand, the client will get the results using 1 query, this is the benefit using GraphQL. But the server will execute 2 queries, one for the user and another one for his comments.
My question is, what are the best practices for building the GraphQL schema and types and combining join between tables, so that the server could also execute the query with 1 request?
The concept you are refering to is called batching. There are several libraries out there that offer this. For example:
Dataloader: generic utility maintained by Facebook that provides "a consistent API over various backends and reduce requests to those backends via batching and caching"
join-monster: "A GraphQL-to-SQL query execution layer for batch data fetching."
To anyone using .NET and the GraphQL for .NET package, I have made an extension method that converts the GraphQL Query into Entity Framework Includes.
public static class ResolveFieldContextExtensions
{
public static string GetIncludeString(this ResolveFieldContext<object> source)
{
return string.Join(',', GetIncludePaths(source.FieldAst));
}
private static IEnumerable<Field> GetChildren(IHaveSelectionSet root)
{
return root.SelectionSet.Selections.Cast<Field>()
.Where(x => x.SelectionSet.Selections.Any());
}
private static IEnumerable<string> GetIncludePaths(IHaveSelectionSet root)
{
var q = new Queue<Tuple<string, Field>>();
foreach (var child in GetChildren(root))
q.Enqueue(new Tuple<string, Field>(child.Name.ToPascalCase(), child));
while (q.Any())
{
var node = q.Dequeue();
var children = GetChildren(node.Item2).ToList();
if (children.Any())
{
foreach (var child in children)
q.Enqueue(new Tuple<string, Field>
(node.Item1 + "." + child.Name.ToPascalCase(), child));
}
else
{
yield return node.Item1;
}
}}}
Lets say we have the following query:
query {
getHistory {
id
product {
id
category {
id
subCategory {
id
}
subAnything {
id
}
}
}
}
}
We can create a variable in "resolve" method of the field:
var include = context.GetIncludeString();
which generates the following string:
"Product.Category.SubCategory,Product.Category.SubAnything"
and pass it to Entity Framework:
public Task<TEntity> Get(TKey id, string include)
{
var query = Context.Set<TEntity>();
if (!string.IsNullOrEmpty(include))
{
query = include.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Aggregate(query, (q, p) => q.Include(p));
}
return query.SingleOrDefaultAsync(c => c.Id.Equals(id));
}

Building FAQ lists with Keystone Lists

Here is the pseudo-code of what I want:
FAQ = {
name: 'Foobar FAQ',
items:[
//question/answer pairs here
]
}
How can I accomplish this in Keystone?
Here's what I've got so far:
var keystone = require('keystone');
var Types = keystone.Field.Types;
var FAQ = new keystone.List('FAQ',{
track: true
});
FAQ.add({
name: {type: String}
items: {} // ???
});
FAQ.register();
I'm unsure how to accomplish this. I'm brand new to React, Keystonejs and Mongodb.
This can be done through a Relationship field type.
items: { type: Types.Relationship, ref: 'Pair', many: true }
where Pair is the name of your Question/Answer pair list object.
For more info, see: http://keystonejs.com/docs/database/#relationships

Sencha touch 2 - Displaying a nested Model in a DataView

I have a model that represents a user. The user has some personal fields (name, family) and another Model as hasMany (his phones, for example).
Something like that:
Ext.define('MyApp.model.User', {
extend: 'Ext.data.Model',
config: {
fields: ['ID', 'Name', 'Family'],
hasMany: { model: 'MyApp.model.Phone', name: 'Phones', associationKey: 'phones' }
}
});
Ext.define('MyApp.model.Phone', {
extend: 'Ext.data.Model',
config: {
fields: ['ID', 'Phone', 'Type']
}
});
Now, I'd like to load it to a dataview (which I did successfully) and would like to show the user info, then the list of the phones that belong to him and when a user taps a phone - to show it using a layout (or message, or anything - the point is to get it's ID)
The problem is that when I use itemtap it gets me the whole record, including the user info and all the phones - so I can't really know what phone was tapped.
To show the user info I just use the itemTpl that shows the info and then lists all the phones.
So - how can I know what phone was tapped?
Thanks!
Roman
There is no clean way to do this, unless you directly bind the phones (userRecord.phones()) store to the dataview. Instead, you will need to get he target of the itemtap event and check what element that is.
itemtap: function(me, index, target, record, e) {
var el = Ext.get(e.target);
alert(el.dom.innerHTML);
}
Full example:
Ext.application({
name: 'Sencha',
launch: function() {
Ext.create('Ext.DataView', {
fullscreen: true,
store: {
fields: ['name'],
data: [
{ name: 'one' },
{ name: 'two' }
]
},
itemTpl: '{name} <span>tap me</span>',
listeners: {
itemtap: function(me, index, target, record, e) {
var el = Ext.get(e.target);
alert(el.dom.innerHTML);
}
}
});
}
});