Skip to content

Commit

Permalink
add get most liked
Browse files Browse the repository at this point in the history
  • Loading branch information
Leouarz committed Feb 7, 2022
1 parent c18198b commit c7f5924
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 36 deletions.
15 changes: 14 additions & 1 deletion src/api/controllers/nfts/controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import NFTService from "../../services/nft";
import { NextFunction, Request, Response } from "express";
import { validationGetNFTs, validationGetNFT, validationGetStatNFTsUser, validationNFTsBySeries, validationGetSeries, validationCanAddToSeries, validationGetHistory, validationAddCategoriesNFTs, validationGetTotalOnSale, validationLikeUnlike } from "../../validators/nftValidators";
import { validationGetNFTs, validationGetNFT, validationGetStatNFTsUser, validationNFTsBySeries, validationGetSeries, validationCanAddToSeries, validationGetHistory, validationAddCategoriesNFTs, validationGetTotalOnSale, validationLikeUnlike, validationGetFilters } from "../../validators/nftValidators";
import { decryptCookie } from "../../../utils";

export class Controller {
Expand Down Expand Up @@ -152,6 +152,19 @@ export class Controller {
next(err)
}
}

async getMostLiked(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const queryValues = validationGetFilters(req.query)
res.json(await NFTService.getMostLiked(queryValues));
} catch (err) {
next(err)
}
}
}

export default new Controller();
4 changes: 4 additions & 0 deletions src/api/controllers/nfts/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import controller from "./controller";
export default express
.Router()
.get("/", controller.getNFTs)
.get("/most-liked", controller.getMostLiked)
// .get("/most-viewed", controller.getMostViewed)
// .get("/most-sold", controller.getMostSold)
// .get("/most-soldSeries", controller.getMostSoldSeries)
.get("/history", controller.getHistory)
.get("/total-on-sale", controller.getTotalOnSale)
.get("/:id", controller.getNFT)
Expand Down
2 changes: 1 addition & 1 deletion src/api/services/gqlQueriesBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export class GQLQueriesBuilder {
${query.filter?.idsToExcludeCategories ? `{id: { notIn: ${JSON.stringify(query.filter.idsToExcludeCategories.map(x => String(x)))} }}` : ""}
${query.filter?.series ? `{serieId: { in: ${JSON.stringify(query.filter.series)} }}` : ""}
${query.filter?.creator ? `{creator: {equalTo: "${query.filter.creator}"}}` : ""}
${query.filter?.isCapsule !== undefined ? `{isCapsule: {isEqual: ${query.filter.isCapsule}}}` : ""}
${query.filter?.priceStartRange !== undefined ?
`{priceRounded: {greaterThanOrEqualTo: "${query.filter.priceStartRange}"}}`
: ""}
Expand All @@ -53,6 +52,7 @@ export class GQLQueriesBuilder {
${query.filter?.owner ? `owner: "${query.filter.owner}"` : ""}
${query.filter?.marketplaceId!==undefined ? `marketplaceId: "${query.filter.marketplaceId}"` : ""}
${query.filter?.listed!==undefined ? `listed: ${!query.filter.listed ? 0 : 1}` : ""}
${query.filter?.isCapsule!==undefined ? `isCapsule: ${!query.filter.isCapsule ? false : true}` : ""}
${query.sort ? `orderBy: [${convertSortString(query.sort)}]` : ""}
) {
totalCount
Expand Down
85 changes: 61 additions & 24 deletions src/api/services/nft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import CategoryService from "./category"
import { populateNFT } from "../helpers/nftHelpers";
import QueriesBuilder from "./gqlQueriesBuilder";
import { decryptCookie, TIME_BETWEEN_SAME_USER_VIEWS } from "../../utils";
import { canAddToSeriesQuery, addCategoriesNFTsQuery, getHistoryQuery, getSeriesStatusQuery, NFTBySeriesQuery, NFTQuery, NFTsQuery, statNFTsUserQuery, getTotalOnSaleQuery, likeUnlikeQuery } from "../validators/nftValidators";
import { canAddToSeriesQuery, addCategoriesNFTsQuery, getHistoryQuery, getSeriesStatusQuery, NFTBySeriesQuery, NFTQuery, NFTsQuery, statNFTsUserQuery, getTotalOnSaleQuery, likeUnlikeQuery, getFiltersQuery } from "../validators/nftValidators";
import CategoryModel from "../../models/category";
import { ICategory } from "../../interfaces/ICategory";
import { INftLike } from "src/interfaces/INftLike";
Expand All @@ -23,31 +23,12 @@ export class NFTService {
*/
async getNFTs(query: NFTsQuery): Promise<CustomResponse<INFT>> {
try {
/// FILTERS
// Categories
if (query.filter?.categories) {
const withNoCategories = query.filter.categories.includes("none")
const categoriesCode = query.filter.categories.filter(x => x !== "none")
const allCategories = await CategoryService.getCategories({})
const categories = allCategories.map(x => x.code).filter(x => categoriesCode.includes(x))
const mongoQuery = { categories: { $in: categories } }
const mongoNfts = await NftModel.find(mongoQuery as any)
const nftIds = mongoNfts.map((nft) => nft.chainId)
if (withNoCategories) {
const categoriesToExclude = allCategories.map(x => x.code).filter(x => !categoriesCode.includes(x))
const mongoQueryExclude = { $and: [{ categories: { $in: categoriesToExclude } }, { chainId: { $nin: nftIds } }] }
const mongoNFTsToExclude = await NftModel.find(mongoQueryExclude as any)
const nftIdsToExclude = mongoNFTsToExclude.map((nft) => nft.chainId)
query.filter.idsToExcludeCategories = nftIdsToExclude
} else {
query.filter.idsCategories = nftIds
}
}
if (query.filter?.categories) this.handleFilterCategory(query)
// Liked only
if (query.filter?.liked) {
const likes = await NftLikeModel.find({walletId: query.filter.liked})
query.filter.series = likes.length > 0 ? likes.map(x => x.serieId) : []
}
// Indexer data
if (query.filter?.liked) this.handleFilterLikedOnly(query)
/// Indexer data
const gqlQuery = QueriesBuilder.NFTs(query);
const res: DistinctNFTListResponse = await request(indexerUrl, gqlQuery);
const NFTs = res.distinctSerieNfts.nodes;
Expand All @@ -67,6 +48,30 @@ export class NFTService {
}
}

async handleFilterCategory(query: NFTsQuery) {
const withNoCategories = query.filter.categories.includes("none")
const categoriesCode = query.filter.categories.filter(x => x !== "none")
const allCategories = await CategoryService.getCategories({})
const categories = allCategories.map(x => x.code).filter(x => categoriesCode.includes(x))
const mongoQuery = { categories: { $in: categories } }
const mongoNfts = await NftModel.find(mongoQuery as any)
const nftIds = mongoNfts.map((nft) => nft.chainId)
if (withNoCategories) {
const categoriesToExclude = allCategories.map(x => x.code).filter(x => !categoriesCode.includes(x))
const mongoQueryExclude = { $and: [{ categories: { $in: categoriesToExclude } }, { chainId: { $nin: nftIds } }] }
const mongoNFTsToExclude = await NftModel.find(mongoQueryExclude as any)
const nftIdsToExclude = mongoNFTsToExclude.map((nft) => nft.chainId)
query.filter.idsToExcludeCategories = nftIdsToExclude
} else {
query.filter.idsCategories = nftIds
}
}

async handleFilterLikedOnly(query: NFTsQuery) {
const likes = await NftLikeModel.find({walletId: query.filter.liked})
query.filter.series = likes.length > 0 ? likes.map(x => x.serieId) : []
}

/**
* Requests a single NFT from the indexer
* @param query - query (see NFTQuery)
Expand Down Expand Up @@ -384,6 +389,38 @@ export class NFTService {
throw new Error("Couldn't unlike NFT");
}
}

/**
* Get NFTs sorted by likes
* @param query - see getFiltersQuery
* @throws Will throw an error if mongo can't be reached
*/
async getMostLiked(query: getFiltersQuery): Promise<CustomResponse<INFT>> {
try {
const aggregateQuery = [{ $group: { _id: "$serieId", totalLikes: { $sum: 1 } } }]
const aggregate = NftLikeModel.aggregate(aggregateQuery);
const data = await NftLikeModel.aggregatePaginate(aggregate, {page: query.pagination.page, limit: query.pagination.limit, sort:{totalLikes: -1}})
const seriesSorted = data.docs.map(x => x._id)
const queryNfts = {filter:{series: seriesSorted}}
const gqlQuery = QueriesBuilder.NFTs(queryNfts);
const res: DistinctNFTListResponse = await request(indexerUrl, gqlQuery);
const NFTs = res.distinctSerieNfts.nodes;
res.distinctSerieNfts.nodes = await Promise.all(NFTs.map(async (NFT) => populateNFT(NFT, query)))
const result: CustomResponse<INFT> = {
totalCount: data.totalDocs,
data: NFTs.sort((a,b) => {
const aId = seriesSorted.findIndex(x => x === a.serieId)
const bId = seriesSorted.findIndex(x => x === b.serieId)
return aId - bId
}),
hasNextPage: data.hasNextPage || undefined,
hasPreviousPage: data.hasPrevPage || undefined
}
return result
} catch (err) {
throw new Error("Couldn't get most liked NFTs");
}
}
}

export default new NFTService();
20 changes: 19 additions & 1 deletion src/api/validators/nftValidators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,22 @@ export const validationLikeUnlike = (query: any) => {
seriesId: Joi.string().required(),
});
return validateQuery(validationSchema, query) as likeUnlikeQuery;
};
};

export type getFiltersQuery = {
pagination: {
page: number
limit: number
}
}
export const validationGetFilters = (query: any) => {
let { pagination } = query;
if (pagination) pagination = JSON.parse(pagination);
const validationSchema = Joi.object({
pagination: Joi.object({
page: Joi.number().integer().min(0).required(),
limit: Joi.number().integer().min(0).max(LIMIT_MAX_PAGINATION).required(),
}).required()
})
return validateQuery(validationSchema, { pagination }) as getFiltersQuery;
}
2 changes: 1 addition & 1 deletion src/models/nftLike.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import mongoose, {AggregatePaginateModel} from "mongoose";
import { INftLike } from "../interfaces/INftLike";
import mongoose, {AggregatePaginateModel} from "mongoose";
import aggregatePaginate from "mongoose-aggregate-paginate-v2";

const NftLike = new mongoose.Schema({
Expand Down
8 changes: 4 additions & 4 deletions src/models/nftView.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { INftView } from "../interfaces/INftView";
import mongoose, { PaginateModel } from "mongoose";
import mongoosePaginate from "mongoose-paginate-v2";
import mongoose, { AggregatePaginateModel } from "mongoose";
import aggregatePaginate from "mongoose-aggregate-paginate-v2";

const NftView = new mongoose.Schema({
viewedSerie: {
Expand All @@ -23,12 +23,12 @@ const NftView = new mongoose.Schema({
},
});

NftView.plugin(mongoosePaginate);
NftView.plugin(aggregatePaginate);

const NftViewModel = mongoose.model<INftView & mongoose.Document>(
"NftView",
NftView,
"nft-views"
) as PaginateModel<INftView & mongoose.Document>;
) as unknown as AggregatePaginateModel<INftView & mongoose.Document>;

export default NftViewModel;
8 changes: 4 additions & 4 deletions src/models/userView.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IUserView } from "../interfaces/IUserView";
import mongoose, { PaginateModel } from "mongoose";
import mongoosePaginate from "mongoose-paginate-v2";
import mongoose, { AggregatePaginateModel } from "mongoose";
import aggregatePaginate from "mongoose-aggregate-paginate-v2";

const UserView = new mongoose.Schema({
viewed: {
Expand All @@ -19,12 +19,12 @@ const UserView = new mongoose.Schema({
},
});

UserView.plugin(mongoosePaginate);
UserView.plugin(aggregatePaginate);

const UserViewModel = mongoose.model<IUserView & mongoose.Document>(
"UserView",
UserView,
"user-views"
) as PaginateModel<IUserView & mongoose.Document>;
) as unknown as AggregatePaginateModel<IUserView & mongoose.Document>;

export default UserViewModel;

0 comments on commit c7f5924

Please sign in to comment.