From 2071e120e7aece92b141061e7cf90cb87d1b0d6e Mon Sep 17 00:00:00 2001 From: Machiko Yasuda Date: Tue, 12 Feb 2019 17:06:06 -0800 Subject: [PATCH] feat(catalogItemsAggregate): add catalogItemsAggregate to sort products by featuredProductIds list. Signed-off-by: Machiko Yasuda --- .../queries/catalogItemsAggregate.js | 69 +++++++++++++++++++ .../catalog/server/no-meteor/queries/index.js | 2 + .../no-meteor/resolvers/Query/catalogItems.js | 5 +- .../server/no-meteor/schemas/schema.graphql | 2 +- 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 imports/plugins/core/catalog/server/no-meteor/queries/catalogItemsAggregate.js diff --git a/imports/plugins/core/catalog/server/no-meteor/queries/catalogItemsAggregate.js b/imports/plugins/core/catalog/server/no-meteor/queries/catalogItemsAggregate.js new file mode 100644 index 00000000000..75f9e91b687 --- /dev/null +++ b/imports/plugins/core/catalog/server/no-meteor/queries/catalogItemsAggregate.js @@ -0,0 +1,69 @@ +import ReactionError from "@reactioncommerce/reaction-error"; + +/** + * @name catalogItemsAggregate + * @method + * @memberof Catalog/NoMeteorQueries + * @summary query the Catalog by shop ID and/or tag ID in featured product order + * @param {Object} context - an object containing the per-request state + * @param {Object} params - request parameters + * @param {String[]} [params.shopIds] - Shop IDs to include + * @param {String} tagId - Tag ID + * @return {Promise} - A MongoDB cursor for the proper query + */ +export default async function catalogItemsAggregate(context, { shopIds, tagId } = {}) { + const { collections } = context; + const { Catalog, Tags } = collections; + + if ((!shopIds || shopIds.length === 0) && (!tagId)) { + throw new ReactionError("invalid-param", "You must provide a tagId or shopIds or both"); + } + + const tag = await Tags.findOne({ + _id: tagId + }); + + if (!tag) { + throw new ReactionError("not-found", "Tag not found"); + } + + // Get array of featured products ids, in order + const order = tag.featuredProductIds; + + // Add a new field "order" to each product with their order in the array + const addFields = { + $addFields: { + "__order": { + "$indexOfArray": [order, "$product._id"] + } + } + }; + + // Projection: Add a featuredPosition by order + const projection = { + $project: { + _id: 1, + __order: 1, + product: 1, + featuredPosition: { + $cond: { + if: { $lt: [ "$__order", 0] }, + then: { $add: [ { $abs: "$__order" }, order.length ]}, + else: "$__order" + } + } + } + }; + + // Sort by featuredPosition + const sort = { + $sort: { + "featuredPosition": 1 + } + }; + + return { + collection: Catalog, + pipeline: [addFields, projection, sort] + } +} diff --git a/imports/plugins/core/catalog/server/no-meteor/queries/index.js b/imports/plugins/core/catalog/server/no-meteor/queries/index.js index 536439c253b..ea44ceca81e 100644 --- a/imports/plugins/core/catalog/server/no-meteor/queries/index.js +++ b/imports/plugins/core/catalog/server/no-meteor/queries/index.js @@ -1,4 +1,5 @@ import catalogItems from "./catalogItems"; +import catalogItemsAggregate from "./catalogItemsAggregate"; import catalogItemProduct from "./catalogItemProduct"; import getCurrentCatalogPriceForProductConfiguration from "./getCurrentCatalogPriceForProductConfiguration"; import tag from "./tag"; @@ -7,6 +8,7 @@ import tagsByIds from "./tagsByIds"; export default { catalogItems, + catalogItemsAggregate, catalogItemProduct, getCurrentCatalogPriceForProductConfiguration, tag, diff --git a/imports/plugins/core/catalog/server/no-meteor/resolvers/Query/catalogItems.js b/imports/plugins/core/catalog/server/no-meteor/resolvers/Query/catalogItems.js index a64d0b625bc..25e422fffa0 100644 --- a/imports/plugins/core/catalog/server/no-meteor/resolvers/Query/catalogItems.js +++ b/imports/plugins/core/catalog/server/no-meteor/resolvers/Query/catalogItems.js @@ -10,7 +10,7 @@ import { decodeTagOpaqueId } from "@reactioncommerce/reaction-graphql-xforms/tag * @param {Object} _ - unused * @param {ConnectionArgs} args - an object of all arguments that were sent by the client * @param {Object} args.shopIds - limit to catalog items for these shops - * @param {Object} args.tagIds - limit to catalog items with these tags + * @param {Array} args.tagIds - limit to catalog items with this array of tags * @param {Object} context - an object containing the per-request state * @return {Promise} A CatalogItemConnection object */ @@ -21,9 +21,10 @@ export default async function catalogItems(_, args, context) { const tagIds = opaqueTagIds && opaqueTagIds.map(decodeTagOpaqueId); if (connectionArgs.sortyBy === "featured") { + const tagId = tagIds[0]; const query = await context.queries.catalogItemsAggregate(context, { shopIds, - tagIds + tagId }); return getPaginatedAggregateResponse(query, connectionArgs); } diff --git a/imports/plugins/core/catalog/server/no-meteor/schemas/schema.graphql b/imports/plugins/core/catalog/server/no-meteor/schemas/schema.graphql index 788dec15933..69e0186d34d 100644 --- a/imports/plugins/core/catalog/server/no-meteor/schemas/schema.graphql +++ b/imports/plugins/core/catalog/server/no-meteor/schemas/schema.graphql @@ -460,7 +460,7 @@ extend type Query { "Provide a Currency code if sortBy is minPrice" sortByPriceCurrencyCode: String - "By default, items are sorted by featured products first, and then when they were created, newest first. Set this to sort by one of the other allowed fields" + "By default, items are sorted by featured products. Set this to sort by one of the other allowed fields" sortBy: CatalogItemSortByField = featured ): CatalogItemConnection "Gets product from catalog"