cnpmcore
Version:
257 lines • 18.8 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageSearchService = void 0;
const tegg_1 = require("@eggjs/tegg");
const elasticsearch_1 = require("@elastic/elasticsearch");
const dayjs_1 = __importDefault(require("dayjs"));
const AbstractService_1 = require("../../common/AbstractService");
const PackageUtil_1 = require("../../common/PackageUtil");
const PackageManagerService_1 = require("./PackageManagerService");
const SearchRepository_1 = require("../../repository/SearchRepository");
const PackageVersionDownloadRepository_1 = require("../../repository/PackageVersionDownloadRepository");
const PackageRepository_1 = require("../../repository/PackageRepository");
const PackageVersionBlockRepository_1 = require("../../repository/PackageVersionBlockRepository");
let PackageSearchService = class PackageSearchService extends AbstractService_1.AbstractService {
async syncPackage(fullname, isSync = true) {
const [scope, name] = (0, PackageUtil_1.getScopeAndName)(fullname);
const fullManifests = await this.packageManagerService.listPackageFullManifests(scope, name, isSync);
if (!fullManifests.data) {
this.logger.warn('[PackageSearchService.syncPackage] save package:%s not found', fullname);
return;
}
const pkg = await this.packageRepository.findPackage(scope, name);
if (!pkg) {
this.logger.warn('[PackageSearchService.syncPackage] findPackage:%s not found', fullname);
return;
}
const block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId);
if (block) {
this.logger.warn('[PackageSearchService.syncPackage] package:%s is blocked, try to remove es', fullname);
await this.removePackage(fullname);
return;
}
// get last year download data
const startDate = (0, dayjs_1.default)().subtract(1, 'year');
const endDate = (0, dayjs_1.default)();
const entities = await this.packageVersionDownloadRepository.query(pkg.packageId, startDate.toDate(), endDate.toDate());
let downloadsAll = 0;
for (const entity of entities) {
for (let i = 1; i <= 31; i++) {
const day = String(i).padStart(2, '0');
const field = `d${day}`;
const counter = entity[field];
if (!counter)
continue;
downloadsAll += counter;
}
}
const { data: manifest } = fullManifests;
const latestVersion = manifest['dist-tags'].latest;
const latestManifest = manifest.versions[latestVersion];
const packageDoc = {
name: manifest.name,
version: latestVersion,
_rev: manifest._rev,
scope: scope ? scope.replace('@', '') : 'unscoped',
keywords: manifest.keywords || [],
versions: Object.keys(manifest.versions),
description: manifest.description,
license: typeof manifest.license === 'object' ? manifest.license?.type : manifest.license,
maintainers: manifest.maintainers,
author: (0, PackageUtil_1.formatAuthor)(manifest.author),
'dist-tags': manifest['dist-tags'],
date: manifest.time[latestVersion],
created: manifest.time.created,
modified: manifest.time.modified,
// 归属 registry,keywords 枚举值
_source_registry_name: manifest._source_registry_name,
// 最新版本发布人 _npmUser:
_npmUser: latestManifest?._npmUser,
// 最新版本发布信息
publish_time: latestManifest?.publish_time,
};
// http://npmmirror.com/package/npm/files/lib/utils/format-search-stream.js#L147-L148
// npm cli 使用 username 字段
if (packageDoc.maintainers) {
packageDoc.maintainers = packageDoc.maintainers.map(maintainer => {
return {
username: maintainer.name,
...maintainer,
};
});
}
const document = {
package: packageDoc,
downloads: {
all: downloadsAll,
},
};
return await this.searchRepository.upsertPackage(document);
}
async searchPackage(text, from, size) {
const matchQueries = this._buildMatchQueries(text);
const scriptScore = this._buildScriptScore({
text,
scoreEffect: 0.25,
});
const res = await this.searchRepository.searchPackage({
body: {
size,
from,
query: {
function_score: {
boost_mode: 'replace',
query: {
bool: {
should: matchQueries,
minimum_should_match: matchQueries.length ? 1 : 0,
},
},
script_score: scriptScore,
},
},
},
});
const { hits, total } = res;
return {
objects: hits?.map(item => {
return item._source;
}),
total: total.value,
};
}
async removePackage(fullname) {
try {
return await this.searchRepository.removePackage(fullname);
}
catch (error) {
// if the package does not exist, returns success
if (error instanceof elasticsearch_1.errors.ResponseError && error?.statusCode === 404) {
this.logger.warn('[PackageSearchService.removePackage] remove package:%s not found', fullname);
return fullname;
}
throw error;
}
}
// https://github.com/npms-io/queries/blob/master/lib/search.js#L8C1-L78C2
_buildMatchQueries(text) {
if (!text) {
return [];
}
return [
// Standard match using cross_fields
{
multi_match: {
query: text,
operator: 'and',
fields: [
'package.name.standard^4',
'package.description.standard',
'package.keywords.standard^2',
],
type: 'cross_fields',
boost: 6,
tie_breaker: 0.5,
},
},
// Partial match using edge-ngram
{
multi_match: {
query: text,
operator: 'and',
fields: [
'package.name.edge_ngram^4',
'package.description.edge_ngram',
'package.keywords.edge_ngram^2',
],
type: 'phrase',
slop: 3,
boost: 3,
tie_breaker: 0.5,
},
},
// Normal term match with an english stemmer
{
multi_match: {
query: text,
operator: 'and',
fields: [
'package.name.english_docs^4',
'package.description.english_docs',
'package.keywords.english_docs^2',
],
type: 'cross_fields',
boost: 3,
tie_breaker: 0.5,
},
},
// Normal term match with a more aggressive english stemmer (not so important)
{
multi_match: {
query: text,
operator: 'and',
fields: [
'package.name.english_aggressive_docs^4',
'package.description.english_aggressive_docs',
'package.keywords.english_aggressive_docs^2',
],
type: 'cross_fields',
tie_breaker: 0.5,
},
},
];
}
_buildScriptScore(params) {
// keep search simple, only download(popularity)
const downloads = 'doc["downloads.all"].value';
const source = `doc["package.name.raw"].value.equals(params.text) ? 100000 + ${downloads} : _score * Math.pow(${downloads}, params.scoreEffect)`;
return {
script: {
source,
params: {
text: params.text || '',
scoreEffect: params.scoreEffect,
},
},
};
}
};
exports.PackageSearchService = PackageSearchService;
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageManagerService_1.PackageManagerService)
], PackageSearchService.prototype, "packageManagerService", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", SearchRepository_1.SearchRepository)
], PackageSearchService.prototype, "searchRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageVersionDownloadRepository_1.PackageVersionDownloadRepository)
], PackageSearchService.prototype, "packageVersionDownloadRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageRepository_1.PackageRepository)
], PackageSearchService.prototype, "packageRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageVersionBlockRepository_1.PackageVersionBlockRepository)
], PackageSearchService.prototype, "packageVersionBlockRepository", void 0);
exports.PackageSearchService = PackageSearchService = __decorate([
(0, tegg_1.SingletonProto)({
accessLevel: tegg_1.AccessLevel.PUBLIC,
})
], PackageSearchService);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGFja2FnZVNlYXJjaFNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hcHAvY29yZS9zZXJ2aWNlL1BhY2thZ2VTZWFyY2hTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7OztBQUFBLHNDQUFrRTtBQUNsRSwwREFBeUQ7QUFDekQsa0RBQTBCO0FBRTFCLGtFQUErRDtBQUMvRCwwREFBeUU7QUFDekUsbUVBQWdFO0FBQ2hFLHdFQUE0RztBQUM1Ryx3R0FBcUc7QUFDckcsMEVBQXVFO0FBQ3ZFLGtHQUErRjtBQU14RixJQUFNLG9CQUFvQixHQUExQixNQUFNLG9CQUFxQixTQUFRLGlDQUFlO0lBWXZELEtBQUssQ0FBQyxXQUFXLENBQUMsUUFBZ0IsRUFBRSxNQUFNLEdBQUcsSUFBSTtRQUMvQyxNQUFNLENBQUUsS0FBSyxFQUFFLElBQUksQ0FBRSxHQUFHLElBQUEsNkJBQWUsRUFBQyxRQUFRLENBQUMsQ0FBQztRQUNsRCxNQUFNLGFBQWEsR0FBRyxNQUFNLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyx3QkFBd0IsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBRXJHLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFO1lBQ3ZCLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLDhEQUE4RCxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQzNGLE9BQU87U0FDUjtRQUVELE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDbEUsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUNSLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLDZEQUE2RCxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQzFGLE9BQU87U0FDUjtRQUVELE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLDZC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