I am implementing pagination in strapi v4. I am using below query.
const favorites = await strapi.db.query("api::gem.gem").findMany({
where: { id: { $in: user.favorites.map((f) => f.id) } },
offset: page > 1 ? (page - 1) * pageSize : 0,
limit: pageSize,
orderBy: { [sort]: order ? order : "asc" },
});
I want to get the count of all records which satisfies the condition.
I have tried passing withCount:true and count:true but nothing worked.
Any help will be much appreciated!
There is a function called findWithCount() which does what you need
const [entries, count] = await strapi.db.query('api::blog.article').findWithCount({
select: ['title', 'description'],
where: { title: 'Hello World' },
orderBy: { title: 'DESC' },
populate: { category: true },
});
source: https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/query-engine/single-operations.html#findwithcount
Related
Strapi Version: 4.4.5
Operating System: linux
Database: sqlite
Node Version: 16.17.0
NPM Version:
Yarn Version: 1.22.19
Hello,
I'm just trying to get all the information from my "Channel" table, namely the product_id and the "users" concerned in the channel. I simply overload my find method like this:
module.exports = createCoreController("api::channel.channel", ({ strapi }) => ({
async find(ctx) {
const { user } = ctx.state:
const entity = await strapi.service("api::channel.channel").find({
filters: {
users: {
id: {
$in: user.id,
},
},
},
populate: ["users"]
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
}));
And for some reason, I get all the user information and especially the hash of the passwords.
So I try to do a select on my populate like this, but it doesn't work :
module.exports = createCoreController("api::channel.channel", ({ strapi }) => ({
async find(ctx) {
const { user } = ctx.state;
const entity = await strapi.service("api::channel.channel").find({
filters: {
users: {
id: {
$in: user.id,
},
},
},
populate: {
users: {
select: ["id"]
}
}
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
}));
Does anyone have a solution to my problem?
It is not select, you would use fields
Strapi Population
const qs = require('qs');
const query = qs.stringify({
fields: ['title', 'body'],
}, {
encodeValuesOnly: true, // prettify URL
});
So in your case, fields in combination with populate.
populate: {
users: {
fields: ["id"]
}
}
Used refPath here documentation: https://mongoosejs.com/docs/populate.html#dynamic-ref
const match = { $or: [{ build: variable }, { country: variable }], isDeleted: undefined };
const populate = [
{ path: `reviewer`, model: 'Build', select: { _id: 1, username: 1, } },
{ path: `reviewer`, model: 'Country', select: { _id: 1, username: 1 } },
];
const buildPopulate = await this.reviewsModel.find(match).select(select).populate(populate[0]).lean().exec();
const countryPopulate = await this.reviewsModel.find(match).select(select).populate(populate[1]).lean().exec();
return { buildPopulate, countryPopulate };
When i was try to populate with just one query i cant get populate[0] already get reviewer null.
I have find this solution but i think is not healthy solution. Anyone have idea ??
I'd love to implement nested pagination within my application. I have been reading the docs and looking at several other examples but I just can't get this to work - any help is appreciated! Thanks!
React component:
I am clicking the button to run the fetchMore function provided by the useQuery hook (apollo). The network request is going through and the new products are merged into the cache... but no new products render on the page.
export const FilterableKit = () => {
const selectedKitId = useReactiveVar(selectedKitIdVar);
const [
getKitProducts,
{ data: getKitProductsData, loading: getKitProductsLoading, fetchMore },
] = useGetKitProductsLazyQuery();
useEffect(() => {
if (selectedKitId) {
getKitProducts({
variables: {
getKitsInput: {
_id: {
string: selectedKitId,
filterBy: "OBJECTID" as StringFilterByEnum,
},
},
getProductsInput: {
config: {
pagination: {
reverse: true,
limit: 3,
},
},
},
},
});
}
}, [getKitProducts, selectedKitId]);
const kitProducts = getKitProductsData?.getKits.data?.find(
(kit) => kit?._id === selectedKitId
)?.products.data;
const handleLoadMore = () => {
if (kitProducts && kitProducts?.length > 0) {
const remaining =
getKitProductsData?.getKits.data[0]?.products.stats?.remaining;
if (remaining && remaining > 0) {
const cursor =
kitProducts[kitProducts.length - 1] &&
kitProducts[kitProducts.length - 1]?.createdAt;
fetchMore({
variables: {
getProductsInput: {
config: {
pagination: {
reverse: true,
createdAt: cursor,
},
},
},
},
});
}
}
};
return (
<CContainer>
<KitItemCards products={kitProducts} loading={getKitProductsLoading} />
<CContainer className="d-flex justify-content-center my-3">
<CButton color="primary" className="w-100" onClick={handleLoadMore}>
Load More
</CButton>
</CContainer>
</CContainer>
);
};
Type Policies: I define the "Kit" typePolicy to merge products into the correct field.
export const cache: InMemoryCache = new InMemoryCache({
typePolicies: {
Kit: {
fields: {
products: {
keyArgs: false,
merge(existing = [] as Product[], incoming: GetProductsResponse) {
if (!incoming) return existing;
if (!existing) return incoming;
const { data: products, ...rest } = incoming;
let result: any = rest;
result = [...existing, ...(products ?? [])];
return result;
},
},
},
},
});
Thanks for any pointers in the right direction! Let me know if there is something else you'd like to see.
I have this function which works:
export const tagsByLabel = async (params) => {
const findManyParams = {
where: { userId: userIdFromSession },
orderBy: { title: "asc" },
};
if (params) {
const { searchTerm } = params;
findManyParams.where.title = { contains: searchTerm };
}
console.log("findManyParams", findManyParams);
const tagsByLabelResult = await db.tag.findMany(findManyParams);
console.log("tagsByLabelResult", tagsByLabelResult);
return tagsByLabelResult;
};
If I search for 'mex', I see:
findManyParams {
where: { userId: 1, title: { contains: 'mex' } },
orderBy: { title: 'asc' }
}
tagsByLabelResult [
{
id: 9,
title: 'mex',
description: 'Mexican food',
userId: 1,
createdAt: 2020-05-03T22:16:09.134Z,
modifiedAt: 2020-05-03T22:16:09.134Z
}
]
And for an empty query, tagsByLabelResult contains all tag records.
How can I adjust my tagsByLabel function to aggregate (using "group by") the records and output a "count" for each record of tagsByLabelResult in order by count descending?
tagsByLabelResult [
{
id: 9,
title: 'mex',
description: 'Mexican food',
count: 25,
userId: 1,
createdAt: 2020-05-03T22:16:09.134Z,
modifiedAt: 2020-05-03T22:16:09.134Z
}
]
I see the docs example of prisma.user.count(), but that seems to retrieve a simple count of the result of the whole query rather than a count as a field with a "group by".
I'm using RedwoodJs, Prisma 2, Apollo, GraphQL.
As of now groupBy support is still in spec here so currently you would only be able to use count with specific querying.
As a workaround, you would have to use prisma.raw for the timebeing.
In my tags.sdl.js I needed to add:
type TagCount {
id: Int!
title: String!
count: Int!
principles: [Principle]
description: String
createdAt: DateTime!
modifiedAt: DateTime!
}
And change query tagsByLabel(searchTerm: String): [Tag!]! to tagsByLabel(searchTerm: String): [TagCount!]!
In my TagsAutocomplete.js component, I now have:
export const TagsAutocomplete = ({ onChange, selectedOptions, closeMenuOnSelect }) => {
const state = {
isLoading: false,
};
const client = useApolloClient();
const promiseOptions = useCallback(
async (searchTerm) => {
try {
const { data } = await client.query({
query: QUERY_TAGS_BY_LABEL,
variables: { searchTerm },
});
console.log("promiseOptions data", data);
const tags = data.tags.map((tag) => {
if (!tag.label.includes("(")) {
//ONEDAY why does the count keep getting appended if this condition isn't checked here?
tag.label = tag.label + " (" + tag.count + ")";
}
return tag;
});
console.log("promiseOptions tags", tags);
return tags;
} catch (e) {
console.error("Error fetching tags", e);
}
},
[client]
);
};
And in my tags.js service, I now have:
export const tagsByLabel = async (params) => {
let query = `
SELECT t.*, COUNT(pt.B) as count FROM tag t LEFT JOIN _PrincipleToTag pt ON t.id = pt.B WHERE t.userId = ${userIdFromSession} `;
if (params) {
const { searchTerm } = params;
if (searchTerm) {
query += `AND t.title LIKE '%${searchTerm}%' `;
}
}
query += "GROUP BY t.id ORDER BY count DESC, t.title ASC;";
console.log("query", query);
const tagsByLabelResult = await db.raw(query);
//TODO get secure parameterization working
console.log("tagsByLabelResult", tagsByLabelResult);
return tagsByLabelResult;
};
But, as mentioned in the comment, I'm still trying to figure out how to get secure parameterization working.
Hi Guys I'm trying to filter post with data json format field?
"categoryList": ["cat", "cat1"]
For anyone still looking for a solution, this is what I have done for a json type field called tags of a collection type called Articles.
I have two articles in the database with one article having the following values set:
title: "lorem ipsum 1",
tags: [
"test",
"rest"
]
The other article has the following values set:
title: "lorem ipsum 2",
tags: [
"test",
"graphql"
]
My graphql query looks like this:
query {
articlesByTag(limit: 2, where: {tags_include: ["test", "rest"]}, start: 0, sort: "title:asc") {
title,
tags
}
}
While my rest query looks like this:
http://localhost:1337/articlesByTag?limit=2&tags_include[]=test&tags_include[]=rest
This is my articles.js service file:
const { convertRestQueryParams, buildQuery } = require('strapi-utils');
const _ = require('lodash');
const { convertToParams, convertToQuery } = require('../../../node_modules/strapi-plugin-graphql/services/utils');
module.exports = {
async findByTag(ctx) {
let tags_include;
if (ctx.where && ctx.where.tags_include && ctx.where.tags_include.length > 0) {
tags_include = ctx.where.tags_include;
delete ctx.where.tags_include;
} else if (ctx.query && ctx.query.tags_include && ctx.query.tags_include.length > 0) {
tags_include = ctx.query.tags_include;
delete ctx.query.tags_include;
}
if (!Array.isArray(tags_include)) {
tags_include = [tags_include];
}
let filters = null;
if (ctx.query) {
filters = convertRestQueryParams({
...convertToParams(ctx.query)
});
} else {
filters = convertRestQueryParams({
...convertToParams(_.pick(ctx, ['limit', 'start', 'sort'])),
...convertToQuery(ctx.where),
});
}
const entities = await strapi.query('articles').model.query(qb => {
buildQuery({ model: strapi.query('articles').model, filters: filters })(qb);
if (tags_include.length > 0) {
tags_include.forEach((tag) => {
if (tag && tag.length > 0) {
const likeStr = `%"${tag}"%`;
qb.andWhere('tags', 'like', likeStr);
}
});
}
}).fetchAll();
return entities;
},
};
This is the entry needed in routes.js
{
"method": "GET",
"path": "/articlesByTag",
"handler": "articles.findByTag",
"config": {
"policies": []
}
}
This is the controller articles.js
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
async findByTag(ctx) {
const entities = await strapi.services.articles.findByTag(ctx);
return entities.map(entity => sanitizeEntity(entity, { model: strapi.models.articles }));
},
};
And finally this is the schema.graphql.js
module.exports = {
query: `
articlesByTag(sort: String, limit: Int, start: Int, where: JSON): [Articles]
`,
resolver: {
Query: {
articlesByTag: {
description: 'Return articles filtered by tag',
resolverOf: 'application::articles.articles.findByTag',
resolver: async (obj, options, ctx) => {
return await strapi.api.articles.controllers.articles.findByTag(options);
},
},
},
},
};
There is not currently a way to filter the JSON fields yet as of beta.17.8 (latest)
Probably something like that?
strapi.query('cool_model').find({ categoryList: { $all: [ "cat" , "cat1" ] } })