#1788 合入统一搜索功能。

Merged
lewis merged 293 commits from zouap into V20220328 2 years ago
  1. +0
    -70
      custom/public/css/git.openi.css
  2. +186
    -0
      models/dbsql/dataset_foreigntable_for_es.sql
  3. +215
    -0
      models/dbsql/issue_foreigntable_for_es.sql
  4. +532
    -0
      models/dbsql/repo_foreigntable_for_es.sql
  5. +308
    -0
      models/dbsql/user_foreigntable_for_es.sql
  6. +1
    -0
      models/models.go
  7. +2
    -1
      models/repo.go
  8. +7
    -3
      models/repo_list.go
  9. +83
    -0
      models/search_record.go
  10. +2
    -1
      modules/setting/setting.go
  11. +12
    -0
      options/locale/locale_en-US.ini
  12. +12
    -0
      options/locale/locale_zh-CN.ini
  13. +1281
    -0
      public/home/search.js
  14. +3
    -3
      public/self/dataset_preview.js
  15. +2
    -0
      routers/init.go
  16. +3
    -0
      routers/routes/routes.go
  17. +1190
    -0
      routers/search.go
  18. +4
    -4
      templates/base/head_navbar.tmpl
  19. +4
    -4
      templates/base/head_navbar_fluid.tmpl
  20. +4
    -4
      templates/base/head_navbar_pro.tmpl
  21. +95
    -0
      templates/explore/search_new.tmpl
  22. +109
    -0
      web_src/less/openi.less

+ 0
- 70
custom/public/css/git.openi.css View File

@@ -44,12 +44,6 @@
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.ui.label{
font-weight: normal;
}
.active {
color: #0366D6 !important;
}

.opacity5{ opacity:0.5;}
.radius15{ border-radius:1.5rem !important; }
@@ -287,70 +281,6 @@
position: relative;
}

/**seach**/
/**搜索导航条适配窄屏**/
.seachnav{
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none; /* firefox */
-ms-overflow-style: none; /* IE 10+ */
}
.seachnav::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
.ui.green.button, .ui.green.buttons .button{
background-color: #5BB973;
}
.seach .repos--seach{
padding-bottom: 0;
border-bottom: none;
}
.seach .ui.secondary.pointing.menu{
border-bottom: none;
}
.seach .ui.secondary.pointing.menu .item > i{
margin-right: 5px;
}
.seach .ui.secondary.pointing.menu .active.item{
border-bottom-width: 2px;
margin: 0 0 -1px;
}
.seach .ui.menu .active.item>.label {
background: #1684FC;
color: #FFF;
}
.seach .ui.menu .item>.label:not(.active.item>.label) {
background: #e8e8e8;
color: rgba(0,0,0,.6);
}

.highlight{
color: red;
}
.ui.list .list>.item>img.image+.content, .ui.list>.item>img.image+.content {
width: calc(100% - 3.0em);
margin-left: 0;
}

.seach .ui.list .list>.item .header, .seach .ui.list>.item .header{
margin-bottom: 0.5em;
font-size: 1.4rem !important;
font-weight: normal;
}
.seach .time, .seach .time a{
font-size: 12px;
color: grey;
}

.seach .list .item.members .ui.avatar.image {
width: 3.2em;
height: 3.2em;
}
.ui.list .list>.item.members>img.image+.content, .ui.list>.item.members>img.image+.content {
width: calc(100% - 4.0em);
margin-left: 0;
}

@media only screen and (max-width: 767px) {
.am-mt-30{ margin-top: 1.5rem !important;}
.ui.secondary.hometop.segment{


+ 186
- 0
models/dbsql/dataset_foreigntable_for_es.sql View File

@@ -0,0 +1,186 @@
DROP FOREIGN TABLE public.dataset_es;
CREATE FOREIGN TABLE public.dataset_es
(
id bigint NOT NULL,
title character varying(255),
status integer,
category character varying(255),
description text,
download_times bigint,
license character varying(255),
task character varying(255),
release_id bigint,
user_id bigint,
repo_id bigint,
created_unix bigint,
updated_unix bigint,
file_name text,
file_desc text
)SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'dataset-es-index',
rowid_column 'id',
default_sort '_id'
)
;
DELETE FROM public.dataset_es;
INSERT INTO public.dataset_es(
id,
title,
status,
category,
description,
download_times,
license, task,
release_id,
user_id,
repo_id,
created_unix,
updated_unix,
file_name,
file_desc
)
SELECT
b.id,
b.title,
b.status,
b.category,
b.description,
b.download_times,
b.license,
b.task,
b.release_id,
b.user_id,
b.repo_id,
b.created_unix,
b.updated_unix,
(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment a where a.dataset_id=b.id and a.is_private=false),
(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment a where a.dataset_id=b.id and a.is_private=false)
FROM public.dataset b,public.repository c where b.repo_id=c.id and c.is_private=false;


DROP TRIGGER IF EXISTS es_insert_dataset on public.dataset;

CREATE OR REPLACE FUNCTION public.insert_dataset_data() RETURNS trigger AS
$def$
DECLARE
privateValue boolean=false;
BEGIN
select into privateValue is_private from public.repository where id=NEW.repo_id;
if not privateValue then
INSERT INTO public.dataset_es(
id,
title,
status,
category,
description,
download_times,
license,
task,
release_id,
user_id,
repo_id,
created_unix,
updated_unix)
VALUES (
NEW.id,
NEW.title,
NEW.status,
NEW.category,
NEW.description,
NEW.download_times,
NEW.license,
NEW.task,
NEW.release_id,
NEW.user_id,
NEW.repo_id,
NEW.created_unix,
NEW.updated_unix
);
end if;
RETURN NEW;
END;
$def$
LANGUAGE plpgsql;



CREATE TRIGGER es_insert_dataset
AFTER INSERT ON public.dataset
FOR EACH ROW EXECUTE PROCEDURE insert_dataset_data();

ALTER TABLE public.dataset ENABLE ALWAYS TRIGGER es_insert_dataset;


DROP TRIGGER IF EXISTS es_udpate_dataset_file_name on public.attachment;

CREATE OR REPLACE FUNCTION public.udpate_dataset_file_name() RETURNS trigger AS
$def$
BEGIN
if (TG_OP = 'UPDATE') then
update public.dataset_es SET file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.dataset_id and is_private=false) where id=NEW.dataset_id;
elsif (TG_OP = 'INSERT') then
update public.dataset_es SET file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.dataset_id and is_private=false) where id=NEW.dataset_id;
elsif (TG_OP = 'DELETE') then
update public.dataset_es SET file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=OLD.dataset_id and is_private=false) where id=OLD.dataset_id;
update public.dataset_es SET file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=OLD.dataset_id and is_private=false) where id=OLD.dataset_id;
end if;
return NEW;
END;
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_udpate_dataset_file_name
AFTER INSERT OR UPDATE OR DELETE ON public.attachment
FOR EACH ROW EXECUTE PROCEDURE udpate_dataset_file_name();

ALTER TABLE public.attachment ENABLE ALWAYS TRIGGER es_udpate_dataset_file_name;

DROP TRIGGER IF EXISTS es_update_dataset on public.dataset;

CREATE OR REPLACE FUNCTION public.update_dataset() RETURNS trigger AS
$def$
BEGIN
UPDATE public.dataset_es
SET description=NEW.description,
title=NEW.title,
category=NEW.category,
task=NEW.task,
download_times=NEW.download_times,
updated_unix=NEW.updated_unix,
file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false),
file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false)
where id=NEW.id;
return new;
END
$def$
LANGUAGE plpgsql;

CREATE TRIGGER es_update_dataset
AFTER UPDATE ON public.dataset
FOR EACH ROW EXECUTE PROCEDURE update_dataset();

ALTER TABLE public.dataset ENABLE ALWAYS TRIGGER es_update_dataset;

DROP TRIGGER IF EXISTS es_delete_dataset on public.dataset;

CREATE OR REPLACE FUNCTION public.delete_dataset() RETURNS trigger AS
$def$
declare
BEGIN
DELETE FROM public.dataset_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_delete_dataset
AFTER DELETE ON public.dataset
FOR EACH ROW EXECUTE PROCEDURE delete_dataset();

ALTER TABLE public.dataset ENABLE ALWAYS TRIGGER es_delete_dataset;

+ 215
- 0
models/dbsql/issue_foreigntable_for_es.sql View File

@@ -0,0 +1,215 @@
DROP FOREIGN TABLE public.issue_es;
CREATE FOREIGN TABLE public.issue_es
(
id bigint NOT NULL,
repo_id bigint,
index bigint,
poster_id bigint,
original_author character varying(255),
original_author_id bigint,
name character varying(255) ,
content text,
comment text,
milestone_id bigint,
priority integer,
is_closed boolean,
is_pull boolean,
pr_id bigint,
num_comments integer,
ref character varying(255),
deadline_unix bigint,
created_unix bigint,
updated_unix bigint,
closed_unix bigint,
is_locked boolean NOT NULL,
amount bigint,
is_transformed boolean NOT NULL
)SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'issue-es-index',
rowid_column 'id',
default_sort '_id'
)
;

delete from public.issue_es;
INSERT INTO public.issue_es(
id,
repo_id,
index,
poster_id,
original_author,
original_author_id,
name,
content,
milestone_id,
priority,
is_closed,
is_pull,
num_comments,
ref,
deadline_unix,
created_unix,
updated_unix,
closed_unix,
is_locked,
amount,
is_transformed,comment,pr_id)
SELECT
b.id,
b.repo_id,
b.index,
b.poster_id,
b.original_author,
b.original_author_id,
b.name,
b.content,
b.milestone_id,
b.priority,
b.is_closed,
b.is_pull,
b.num_comments,
b.ref,
b.deadline_unix,
b.created_unix,
b.updated_unix,
b.closed_unix,
b.is_locked,
b.amount,
b.is_transformed,
(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment a where a.issue_id=b.id),
(select id from public.pull_request d where b.id=d.issue_id and b.is_pull=true)
FROM public.issue b,public.repository c where b.repo_id=c.id and c.is_private=false;


CREATE OR REPLACE FUNCTION public.insert_issue_data() RETURNS trigger AS
$def$
DECLARE
privateValue boolean=false;
BEGIN
select into privateValue is_private from public.repository where id=NEW.repo_id;
if not privateValue then
INSERT INTO public.issue_es(
id,
repo_id,
index,
poster_id,
original_author,
original_author_id,
name,
content,
milestone_id,
priority,
is_closed,
is_pull,
num_comments,
ref,
deadline_unix,
created_unix,
updated_unix,
closed_unix,
is_locked,
amount,
is_transformed)
VALUES (
NEW.id,
NEW.repo_id,
NEW.index,
NEW.poster_id,
NEW.original_author,
NEW.original_author_id,
NEW.name,
NEW.content,
NEW.milestone_id,
NEW.priority,
NEW.is_closed,
NEW.is_pull,
NEW.num_comments,
NEW.ref,
NEW.deadline_unix,
NEW.created_unix,
NEW.updated_unix,
NEW.closed_unix,
NEW.is_locked,
NEW.amount,
NEW.is_transformed
);
end if;
RETURN NEW;
END;
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_insert_issue on public.issue;

CREATE TRIGGER es_insert_issue
AFTER INSERT ON public.issue
FOR EACH ROW EXECUTE PROCEDURE insert_issue_data();

ALTER TABLE public.issue ENABLE ALWAYS TRIGGER es_insert_issue;

CREATE OR REPLACE FUNCTION public.udpate_issue_comment() RETURNS trigger AS
$def$
BEGIN
if (TG_OP = 'DELETE') then
update public.issue_es SET comment=(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment where issue_id=OLD.issue_id) where id=OLD.issue_id;
elsif (TG_OP = 'UPDATE') then
update public.issue_es SET comment=(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment where issue_id=NEW.issue_id) where id=NEW.issue_id;
end if;
return null;
END;
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_udpate_issue_comment on public.comment;
CREATE TRIGGER es_udpate_issue_comment
AFTER DELETE OR UPDATE ON public.comment
FOR EACH ROW EXECUTE PROCEDURE udpate_issue_comment();

ALTER TABLE public.comment ENABLE ALWAYS TRIGGER es_udpate_issue_comment;


CREATE OR REPLACE FUNCTION public.update_issue() RETURNS trigger AS
$def$
declare
BEGIN
UPDATE public.issue_es
SET content=NEW.content,
name=NEW.name,
is_closed=NEW.is_closed,
num_comments=NEW.num_comments,
comment=(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment where issue_id=NEW.id)
where id=NEW.id;
return new;
END
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_update_issue on public.issue;

CREATE TRIGGER es_update_issue
AFTER UPDATE ON public.issue
FOR EACH ROW EXECUTE PROCEDURE update_issue();

ALTER TABLE public.issue ENABLE ALWAYS TRIGGER es_update_issue;

CREATE OR REPLACE FUNCTION public.delete_issue() RETURNS trigger AS
$def$
declare
BEGIN
DELETE FROM public.issue_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_delete_issue on public.issue;
CREATE TRIGGER es_delete_issue
AFTER DELETE ON public.issue
FOR EACH ROW EXECUTE PROCEDURE delete_issue();

ALTER TABLE public.issue ENABLE ALWAYS TRIGGER es_delete_issue;

+ 532
- 0
models/dbsql/repo_foreigntable_for_es.sql View File

@@ -0,0 +1,532 @@
-- 要处理项目从私有变为公有,并且从公有变成私有的情况
DROP FOREIGN table if exists public.repository_es;
CREATE FOREIGN TABLE public.repository_es (
id bigint NOT NULL,
owner_id bigint,
owner_name character varying(255),
lower_name character varying(255) NOT NULL,
name character varying(255) NOT NULL,
description text,
website character varying(2048),
original_service_type integer,
original_url character varying(2048),
default_branch character varying(255),
num_watches integer,
num_stars integer,
num_forks integer,
num_issues integer,
num_closed_issues integer,
num_pulls integer,
num_closed_pulls integer,
num_milestones integer DEFAULT 0 NOT NULL,
num_closed_milestones integer DEFAULT 0 NOT NULL,
is_private boolean,
is_empty boolean,
is_archived boolean,
is_mirror boolean,
status integer DEFAULT 0 NOT NULL,
is_fork boolean DEFAULT false NOT NULL,
fork_id bigint,
is_template boolean DEFAULT false NOT NULL,
template_id bigint,
size bigint DEFAULT 0 NOT NULL,
is_fsck_enabled boolean DEFAULT true NOT NULL,
close_issues_via_commit_in_any_branch boolean DEFAULT false NOT NULL,
topics text,
avatar character varying(64),
created_unix bigint,
updated_unix bigint,
contract_address character varying(255),
block_chain_status integer DEFAULT 0 NOT NULL,
balance character varying(255) DEFAULT '0'::character varying NOT NULL,
clone_cnt bigint DEFAULT 0 NOT NULL,
license character varying(100),
download_cnt bigint DEFAULT 0 NOT NULL,
num_commit bigint DEFAULT 0 NOT NULL,
git_clone_cnt bigint DEFAULT 0 NOT NULL,
creator_id bigint NOT NULL DEFAULT 0,
repo_type integer NOT NULL DEFAULT 0,
lang character varying(2048),
alias character varying(255),
lower_alias character varying(255)
) SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'repository-es-index',
rowid_column 'id',
default_sort '_id'
)
;
delete from public.repository_es;
INSERT INTO public.repository_es (id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
lang,
alias,
lower_alias
)
SELECT
id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat a where a.repo_id=b.id),
alias,
lower_alias
FROM public.repository b where b.is_private=false;

DROP TRIGGER IF EXISTS es_insert_repository on public.repository;

CREATE OR REPLACE FUNCTION public.insert_repository_data() RETURNS trigger AS
$def$
BEGIN
if not NEW.is_private then
INSERT INTO public.repository_es (id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
alias,
lower_alias) VALUES
(NEW.id,
NEW.owner_id,
NEW.owner_name,
NEW.lower_name,
NEW.name,
NEW.description,
NEW.website,
NEW.original_service_type,
NEW.original_url,
NEW.default_branch,
NEW.num_watches,
NEW.num_stars,
NEW.num_forks,
NEW.num_issues,
NEW.num_closed_issues,
NEW.num_pulls,
NEW.num_closed_pulls,
NEW.num_milestones,
NEW.num_closed_milestones,
NEW.is_private,
NEW.is_empty,
NEW.is_archived,
NEW.is_mirror,
NEW.status,
NEW.is_fork,
NEW.fork_id,
NEW.is_template,
NEW.template_id,
NEW.size,
NEW.is_fsck_enabled,
NEW.close_issues_via_commit_in_any_branch,
NEW.topics,
NEW.avatar,
NEW.created_unix,
NEW.updated_unix,
NEW.contract_address,
NEW.block_chain_status,
NEW.balance,
NEW.clone_cnt,
NEW.num_commit,
NEW.git_clone_cnt,
NEW.creator_id,
NEW.repo_type,
NEW.alias,
NEW.lower_alias);
end if;
RETURN NEW;
END;
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_insert_repository
AFTER INSERT ON public.repository
FOR EACH ROW EXECUTE PROCEDURE insert_repository_data();

ALTER TABLE public.repository ENABLE ALWAYS TRIGGER es_insert_repository;

DROP TRIGGER IF EXISTS es_update_repository on public.repository;

CREATE OR REPLACE FUNCTION public.update_repository() RETURNS trigger AS
$def$
BEGIN
if OLD.is_private != NEW.is_private then
if OLD.is_private and not NEW.is_private then
--insert
INSERT INTO public.repository_es (id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
lang,
alias,
lower_alias)
SELECT
id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat a where a.repo_id=b.id),
alias,
lower_alias
FROM public.repository b where b.id=NEW.id;
INSERT INTO public.dataset_es(
id,
title,
status,
category,
description,
download_times,
license, task,
release_id,
user_id,
repo_id,
created_unix,
updated_unix,file_name)
SELECT
b.id,
b.title,
b.status,
b.category,
b.description,
b.download_times,
b.license,
b.task,
b.release_id,
b.user_id,
b.repo_id,
b.created_unix,
b.updated_unix,(select array_to_string(array_agg(name order by created_unix desc),',') from public.attachment a where a.dataset_id=b.id and a.is_private=false)
FROM public.dataset b where b.repo_id=NEW.id;

INSERT INTO public.issue_es(
id,
repo_id,
index,
poster_id,
original_author,
original_author_id,
name,
content,
milestone_id,
priority,
is_closed,
is_pull,
num_comments,
ref,
deadline_unix,
created_unix,
updated_unix,
closed_unix,
is_locked,
amount,
is_transformed,comment,pr_id)
SELECT
b.id,
b.repo_id,
b.index,
b.poster_id,
b.original_author,
b.original_author_id,
b.name,
b.content,
b.milestone_id,
b.priority,
b.is_closed,
b.is_pull,
b.num_comments,
b.ref,
b.deadline_unix,
b.created_unix,
b.updated_unix,
b.closed_unix,
b.is_locked,
b.amount,
b.is_transformed,
(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment a where a.issue_id=b.id),
(select id from public.pull_request d where d.issue_id=b.id)
FROM public.issue b where b.repo_id=NEW.id;
end if;

if not OLD.is_private and NEW.is_private then
delete from public.issue_es where repo_id=NEW.id;
delete from public.dataset_es where repo_id=NEW.id;
delete from public.repository_es where id=NEW.id;
end if;

end if;

if not NEW.is_private then
raise notice 'update repo,the updated_unix is %',NEW.updated_unix;
update public.repository_es SET description=NEW.description,
name=NEW.name,
lower_name=NEW.lower_name,
owner_name=NEW.owner_name,
website=NEW.website,
updated_unix=NEW.updated_unix,
num_watches=NEW.num_watches,
num_stars=NEW.num_stars,
num_forks=NEW.num_forks,
topics=NEW.topics,
alias = NEW.alias,
lower_alias = NEW.lower_alias,
avatar=NEW.avatar
where id=NEW.id;
end if;
return new;
END
$def$
LANGUAGE plpgsql;

CREATE TRIGGER es_update_repository
AFTER UPDATE ON public.repository
FOR EACH ROW EXECUTE PROCEDURE update_repository();

ALTER TABLE public.repository ENABLE ALWAYS TRIGGER es_update_repository;


DROP TRIGGER IF EXISTS es_delete_repository on public.repository;

CREATE OR REPLACE FUNCTION public.delete_repository() RETURNS trigger AS
$def$
declare
BEGIN
delete from public.issue_es where repo_id=OLD.id;
delete from public.dataset_es where repo_id=OLD.id;
DELETE FROM public.repository_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_delete_repository
AFTER DELETE ON public.repository
FOR EACH ROW EXECUTE PROCEDURE delete_repository();

ALTER TABLE public.repository ENABLE ALWAYS TRIGGER es_delete_repository;



DROP TRIGGER IF EXISTS es_udpate_repository_lang on public.language_stat;

CREATE OR REPLACE FUNCTION public.udpate_repository_lang() RETURNS trigger AS
$def$
BEGIN
if (TG_OP = 'UPDATE') then
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id;
elsif (TG_OP = 'INSERT') then
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id;
elsif (TG_OP = 'DELETE') then
if exists(select 1 from public.repository where id=OLD.repo_id) then
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=OLD.repo_id) where id=OLD.repo_id;
end if;
end if;
return null;
END;
$def$
LANGUAGE plpgsql;

CREATE TRIGGER es_udpate_repository_lang
AFTER INSERT OR UPDATE OR DELETE ON public.language_stat
FOR EACH ROW EXECUTE PROCEDURE udpate_repository_lang();

ALTER TABLE public.language_stat ENABLE ALWAYS TRIGGER es_udpate_repository_lang;

+ 308
- 0
models/dbsql/user_foreigntable_for_es.sql View File

@@ -0,0 +1,308 @@
DROP FOREIGN table if exists public.user_es;
CREATE FOREIGN TABLE public.user_es
(
id bigint NOT NULL ,
lower_name character varying(255) NULL,
name character varying(255) NULL,
full_name character varying(255),
email character varying(255),
keep_email_private boolean,
email_notifications_preference character varying(20) ,
passwd character varying(255) ,
passwd_hash_algo character varying(255) ,
must_change_password boolean NOT NULL DEFAULT false,
login_type integer,
login_source bigint NOT NULL DEFAULT 0,
login_name character varying(255) ,
type integer,
location character varying(255),
website character varying(255),
rands character varying(10),
salt character varying(10),
language character varying(5),
description character varying(255),
created_unix bigint,
updated_unix bigint,
last_login_unix bigint,
last_repo_visibility boolean,
max_repo_creation integer,
is_active boolean,
is_admin boolean,
is_restricted boolean NOT NULL DEFAULT false,
allow_git_hook boolean,
allow_import_local boolean,
allow_create_organization boolean DEFAULT true,
prohibit_login boolean NOT NULL DEFAULT false,
avatar character varying(2048) ,
avatar_email character varying(255),
use_custom_avatar boolean,
num_followers integer,
num_following integer NOT NULL DEFAULT 0,
num_stars integer,
num_repos integer,
num_teams integer,
num_members integer,
visibility integer NOT NULL DEFAULT 0,
repo_admin_change_team_access boolean NOT NULL DEFAULT false,
diff_view_style character varying(255),
theme character varying(255),
token character varying(1024) ,
public_key character varying(255),
private_key character varying(255),
is_operator boolean NOT NULL DEFAULT false,
num_dataset_stars integer NOT NULL DEFAULT 0
) SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'user-es-index',
rowid_column 'id',
default_sort '_id'
)
;
delete from public.user_es;
INSERT INTO public.user_es(
id,
lower_name,
name,
full_name,
email,
keep_email_private,
email_notifications_preference,
must_change_password,
login_type,
login_source,
login_name,
type,
location,
website,
rands,
language,
description,
created_unix,
updated_unix,
last_login_unix,
last_repo_visibility,
max_repo_creation,
is_active,
is_restricted,
allow_git_hook,
allow_import_local,
allow_create_organization,
prohibit_login,
avatar,
avatar_email,
use_custom_avatar,
num_followers,
num_following,
num_stars,
num_repos,
num_teams,
num_members,
visibility,
repo_admin_change_team_access,
diff_view_style,
theme,
is_operator,
num_dataset_stars)
SELECT
id,
lower_name,
name,
full_name,
email,
keep_email_private,
email_notifications_preference,
must_change_password,
login_type,
login_source,
login_name,
type,
location,
website,
rands,
language,
description,
created_unix,
updated_unix,
last_login_unix,
last_repo_visibility,
max_repo_creation,
is_active,
is_restricted,
allow_git_hook,
allow_import_local,
allow_create_organization,
prohibit_login,
avatar,
avatar_email,
use_custom_avatar,
num_followers,
num_following,
num_stars,
num_repos,
num_teams,
num_members,
visibility,
repo_admin_change_team_access,
diff_view_style,
theme,
is_operator,
num_dataset_stars
FROM public.user;

DROP TRIGGER IF EXISTS es_insert_user on public.user;

CREATE OR REPLACE FUNCTION public.insert_user_data() RETURNS trigger AS
$def$
BEGIN
INSERT INTO public."user_es"(
id,
lower_name,
name,
full_name,
email,
keep_email_private,
email_notifications_preference,
must_change_password,
login_type,
login_source,
login_name,
type,
location,
website,
rands,
language,
description,
created_unix,
updated_unix,
last_login_unix,
last_repo_visibility,
max_repo_creation,
is_active,
is_restricted,
allow_git_hook,
allow_import_local,
allow_create_organization,
prohibit_login,
avatar,
avatar_email,
use_custom_avatar,
num_followers,
num_following,
num_stars,
num_repos,
num_teams,
num_members,
visibility,
repo_admin_change_team_access,
diff_view_style,
theme,
is_operator,
num_dataset_stars)
VALUES (
NEW.id,
NEW.lower_name,
NEW.name,
NEW.full_name,
NEW.email,
NEW.keep_email_private,
NEW.email_notifications_preference,
NEW.must_change_password,
NEW.login_type,
NEW.login_source,
NEW.login_name,
NEW.type,
NEW.location,
NEW.website,
NEW.rands,
NEW.language,
NEW.description,
NEW.created_unix,
NEW.updated_unix,
NEW.last_login_unix,
NEW.last_repo_visibility,
NEW.max_repo_creation,
NEW.is_active,
NEW.is_restricted,
NEW.allow_git_hook,
NEW.allow_import_local,
NEW.allow_create_organization,
NEW.prohibit_login,
NEW.avatar,
NEW.avatar_email,
NEW.use_custom_avatar,
NEW.num_followers,
NEW.num_following,
NEW.num_stars,
NEW.num_repos,
NEW.num_teams,
NEW.num_members,
NEW.visibility,
NEW.repo_admin_change_team_access,
NEW.diff_view_style,
NEW.theme,
NEW.is_operator,
NEW.num_dataset_stars
);

RETURN NEW;
END;
$def$
LANGUAGE plpgsql;



CREATE TRIGGER es_insert_user
AFTER INSERT ON public.user
FOR EACH ROW EXECUTE PROCEDURE insert_user_data();

ALTER TABLE public.user ENABLE ALWAYS TRIGGER es_insert_user;

DROP TRIGGER IF EXISTS es_update_user on public.user;

CREATE OR REPLACE FUNCTION public.update_user() RETURNS trigger AS
$def$
BEGIN
UPDATE public.user_es
SET description=NEW.description,
name=NEW.name,
full_name=NEW.full_name,
location=NEW.location,
website=NEW.website,
email=NEW.email,
num_dataset_stars=NEW.num_dataset_stars,
updated_unix=NEW.updated_unix
where id=NEW.id;
return new;
END
$def$
LANGUAGE plpgsql;



CREATE TRIGGER es_update_user
AFTER UPDATE ON public.user
FOR EACH ROW EXECUTE PROCEDURE update_user();

ALTER TABLE public.user ENABLE ALWAYS TRIGGER es_update_user;

DROP TRIGGER IF EXISTS es_delete_user on public.user;

CREATE OR REPLACE FUNCTION public.delete_user() RETURNS trigger AS
$def$
declare
BEGIN
DELETE FROM public.user_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_delete_user
AFTER DELETE ON public.user
FOR EACH ROW EXECUTE PROCEDURE delete_user();
ALTER TABLE public.user ENABLE ALWAYS TRIGGER es_delete_user;

+ 1
- 0
models/models.go View File

@@ -138,6 +138,7 @@ func init() {
new(OfficialTag),
new(OfficialTagRepos),
new(WechatBindLog),
new(SearchRecord),
)

tablesStatistic = append(tablesStatistic,


+ 2
- 1
models/repo.go View File

@@ -6,13 +6,14 @@
package models

import (
"code.gitea.io/gitea/modules/git"
"context"
"crypto/md5"
"errors"
"fmt"
"html/template"
"math/rand"

"code.gitea.io/gitea/modules/git"
"xorm.io/xorm"

"code.gitea.io/gitea/modules/blockchain"


+ 7
- 3
models/repo_list.go View File

@@ -190,7 +190,8 @@ type SearchRepoOptions struct {
// None -> include all repos
// True -> include just courses
// False -> include just no courses
Course util.OptionalBool
Course util.OptionalBool
OnlySearchPrivate bool
}

//SearchOrderBy is used to sort the result
@@ -219,12 +220,15 @@ const (
SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC"
SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC"
SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC"
SearchOrderByWatches SearchOrderBy = "num_watches DESC"
)

// SearchRepositoryCondition creates a query condition according search repository options
func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
var cond = builder.NewCond()

if opts.OnlySearchPrivate {
cond = cond.And(builder.Eq{"is_private": true})
}
if opts.Private {
if opts.Actor != nil && !opts.Actor.IsAdmin && opts.Actor.ID != opts.OwnerID {
// OK we're in the context of a User
@@ -337,7 +341,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
if !opts.TopicOnly {
var likes = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {
likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
likes = likes.Or(builder.Like{"lower_alias", strings.ToLower(v)})
likes = likes.Or(builder.Like{"alias", v})
if opts.IncludeDescription {
likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})


+ 83
- 0
models/search_record.go View File

@@ -0,0 +1,83 @@
package models

import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)

type SearchRecord struct {
ID int64 `xorm:"pk autoincr"`
//user
Keyword string `xorm:"NOT NULL"`
//
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
}

func SaveSearchKeywordToDb(keyword string) error {
record := &SearchRecord{
Keyword: keyword,
}
sess := x.NewSession()
defer sess.Close()
_, err := sess.Insert(record)
if err != nil {
log.Info("insert error." + err.Error())
return err
}
return nil
}

func setIssueQueryCondition(sess *xorm.Session, Keyword string, isPull bool, userId int64) {
sess.And("issue.poster_id=?", userId)
sess.And("issue.is_pull=?", isPull)
sess.And("(issue.name like '%" + Keyword + "%' or issue.content like '%" + Keyword + "%')")
sess.Join("INNER", "repository", "issue.repo_id = repository.id").And("repository.is_private = ?", true)
}

func SearchPrivateIssueOrPr(Page int, PageSize int, Keyword string, isPull bool, userId int64) ([]*Issue, int64, error) {
sess := x.NewSession()
defer sess.Close()
setIssueQueryCondition(sess, Keyword, isPull, userId)
count, err := sess.Count(new(Issue))
if err != nil {
return nil, 0, err
}

setIssueQueryCondition(sess, Keyword, isPull, userId)
sess.Desc("issue.created_unix")
sess.Limit(PageSize, (Page-1)*PageSize)
issues := make([]*Issue, 0)
if err := sess.Find(&issues); err != nil {
return nil, 0, err
} else {
return issues, count, nil
}
}

func setDataSetQueryCondition(sess *xorm.Session, Keyword string, userId int64) {
sess.And("dataset.user_id=?", userId)
sess.And("(dataset.title like '%" + Keyword + "%' or dataset.description like '%" + Keyword + "%')")
sess.Join("INNER", "repository", "dataset.repo_id = repository.id").And("repository.is_private = ?", true)
}

func SearchDatasetBySQL(Page int, PageSize int, Keyword string, userId int64) ([]*Dataset, int64, error) {
sess := x.NewSession()
defer sess.Close()
setDataSetQueryCondition(sess, Keyword, userId)
count, err := sess.Count(new(Dataset))
if err != nil {
return nil, 0, err
}

setDataSetQueryCondition(sess, Keyword, userId)
sess.Desc("dataset.created_unix")
sess.Limit(PageSize, (Page-1)*PageSize)
datasets := make([]*Dataset, 0)
if err := sess.Find(&datasets); err != nil {
return nil, 0, err
} else {
return datasets, count, nil
}

}

+ 2
- 1
modules/setting/setting.go View File

@@ -437,7 +437,7 @@ var (

//home page
RecommentRepoAddr string
ESSearchURL string
//notice config
UserNameOfNoticeRepo string
RepoNameOfNoticeRepo string
@@ -1267,6 +1267,7 @@ func NewContext() {

sec = Cfg.Section("homepage")
RecommentRepoAddr = sec.Key("Address").MustString("https://git.openi.org.cn/OpenIOSSG/promote/raw/branch/master/")
ESSearchURL = sec.Key("ESSearchURL").MustString("http://192.168.207.94:9200")

sec = Cfg.Section("notice")
UserNameOfNoticeRepo = sec.Key("USER_NAME").MustString("OpenIOSSG")


+ 12
- 0
options/locale/locale_en-US.ini View File

@@ -254,6 +254,18 @@ page_dev_yunlao_desc3=Developers can freely choose the corresponding computing r
page_dev_yunlao_desc4=If your model requires more computing resources, you can also apply for it separately.
page_dev_yunlao_apply=Apply Separately

search=Search
search_repo=Repository
search_dataset=DataSet
search_issue=Issue
search_pr=Pull Request
search_user=User
search_org=Organization
search_finded=Find
search_related=related
search_maybe=maybe
search_ge=

[explore]
repos = Repositories
select_repos = Select the project


+ 12
- 0
options/locale/locale_zh-CN.ini View File

@@ -256,6 +256,18 @@ page_dev_yunlao_desc3=开发者可以根据使用需求,自由选择相应计
page_dev_yunlao_desc4=如果您的模型需要更多的计算资源,也可以单独申请
page_dev_yunlao_apply=单独申请

search=搜索
search_repo=项目
search_dataset=数据集
search_issue=任务
search_pr=合并请求
search_user=用户
search_org=组织
search_finded=找到
search_related=相关
search_maybe=约为
search_ge=个

[explore]
repos=项目
select_repos=精选项目


+ 1281
- 0
public/home/search.js View File

@@ -0,0 +1,1281 @@
var token;
if(isEmpty(token)){
var meta = $("meta[name=_uid]");
if(!isEmpty(meta)){
token = meta.attr("content");
console.log("token is uid:" + token);
}
}

var html =document.documentElement;
var lang = html.attributes["lang"]
var isZh = true;
if(lang != null && lang.nodeValue =="en-US" ){
console.log("the language is " + lang.nodeValue);
isZh=false;
}else{
console.log("default lang=zh");
}
function isEmpty(str){
if(typeof str == "undefined" || str == null || str == ""){
return true;
}
return false;
}

var itemType={
"1":"repository",
"2":"issue",
"3":"user",
"4":"org",
"5":"dataset",
"6":"pr"
};

var sortBy={
"10":"default",
"11":"updated_unix.keyword",
"12":"num_watches",
"13":"num_stars",
"14":"num_forks",
"20":"default",
"21":"updated_unix.keyword",
"30":"default",
"31":"name.keyword",
"32":"name.keyword",
"33":"created_unix.keyword",
"34":"created_unix.keyword",
"40":"default",
"41":"name.keyword",
"42":"name.keyword",
"43":"created_unix.keyword",
"44":"created_unix.keyword",
"50":"default",
"51":"download_times",
"60":"default",
"61":"updated_unix.keyword"
};

var sortAscending={
"10":"false",
"11":"false",
"12":"false",
"13":"false",
"14":"false",
"20":"false",
"21":"false",
"30":"false",
"31":"true",
"32":"false",
"33":"false",
"34":"true",
"40":"false",
"41":"true",
"42":"false",
"43":"false",
"44":"true",
"50":"false",
"51":"false",
"60":"false",
"61":"false"
};

var currentPage = 1;
var pageSize = 15;
var currentSearchTableName ="repository";
var currentSearchKeyword="";
var currentSearchSortBy="";
var currentSearchAscending="false";
var OnlySearchLabel=false;
var startIndex =1;
var endIndex = 5;
var totalPage = 1;
var totalNum = 0;
var privateTotal = 0;

function initPageInfo(){
currentPage = 1;
startIndex =1;
endIndex = 5;
}

function searchItem(type,sortType){
console.log("enter item 2.");
currentSearchKeyword = document.getElementById("keyword_input").value;
if(!isEmpty(currentSearchKeyword)){
initPageInfo();
currentSearchTableName = itemType[type];
currentSearchSortBy = sortBy[sortType];
currentSearchAscending = sortAscending[sortType];
OnlySearchLabel =false;
page(currentPage);
}
}



function search(){
console.log("enter here 1.");
currentSearchKeyword = document.getElementById("keyword_input").value;
if(!isEmpty(currentSearchKeyword)){
currentSearchKeyword = currentSearchKeyword.trim();
}
$('#searchForm').addClass("hiddenSearch");
initPageInfo();
if(!isEmpty(currentSearchKeyword)){
document.getElementById("find_id").innerHTML=getLabel(isZh,"search_finded");
currentSearchSortBy = sortBy[10];
currentSearchAscending = "false";
OnlySearchLabel =false;
page(currentPage);
if(currentSearchTableName != "repository"){
doSearch("repository",currentSearchKeyword,1,pageSize,true,"",false);
}
if(currentSearchTableName != "issue"){
doSearch("issue",currentSearchKeyword,1,pageSize,true,"",false);
}
if(currentSearchTableName != "user"){
doSearch("user",currentSearchKeyword,1,pageSize,true,"",false);
}
if(currentSearchTableName != "org"){
doSearch("org",currentSearchKeyword,1,pageSize,true,"",false);
}
if(currentSearchTableName != "dataset"){
doSearch("dataset",currentSearchKeyword,1,pageSize,true,"",false);
}
if(currentSearchTableName != "pr"){
doSearch("pr",currentSearchKeyword,1,pageSize,true,"",false);
}
}else{
initDiv(false);
document.getElementById("find_id").innerHTML=getLabel(isZh,"search_empty");
$('#find_title').html("");
document.getElementById("sort_type").innerHTML="";
document.getElementById("child_search_item").innerHTML="";
document.getElementById("page_menu").innerHTML="";
$('#repo_total').text("");
$('#pr_total').text("");
$('#issue_total').text("");
$('#dataset_total').text("");
$('#user_total').text("");
$('#org_total').text("");
setActivate(null);
}
}

function initDiv(isSearchLabel=false){
if(isSearchLabel){
document.getElementById("search_div").style.display="none";
document.getElementById("search_label_div").style.display="block";
document.getElementById("dataset_item").style.display="none";
document.getElementById("issue_item").style.display="none";
document.getElementById("pr_item").style.display="none";
document.getElementById("user_item").style.display="none";
document.getElementById("org_item").style.display="none";
document.getElementById("find_id").innerHTML="";
}else{
document.getElementById("search_div").style.display="block";
document.getElementById("search_label_div").style.display="none";
document.getElementById("dataset_item").style.display="block";
document.getElementById("issue_item").style.display="block";
document.getElementById("pr_item").style.display="block";
document.getElementById("user_item").style.display="block";
document.getElementById("org_item").style.display="block";
document.getElementById("find_id").innerHTML=getLabel(isZh,"search_finded");
}
}

function doSearchLabel(tableName,keyword,sortBy="",ascending="false"){
initDiv(true);
//document.getElementById("search_div").style.display="none";
//document.getElementById("search_label_div").style.display="block";
document.getElementById("search_label_div").innerHTML="<p class=\"searchlabel\">#" + keyword + "</p>";
currentSearchKeyword = keyword;
initPageInfo();
currentSearchTableName = tableName;
currentSearchSortBy = sortBy;
currentSearchAscending = ascending;
OnlySearchLabel =true;
page(currentPage);
}

function searchLabel(tableName,keyword,sortBy="",ascending="false"){

sessionStorage.setItem("keyword",keyword);
sessionStorage.setItem("tableName",tableName);
sessionStorage.setItem("searchLabel",true);
sessionStorage.setItem("sortBy",sortBy);
sessionStorage.setItem("ascending",ascending);
console.log("enter label search.");
window.open("/all/search/");
}

function doSearch(tableName,keyword,page,pageSize=15,onlyReturnNum=true,sortBy="",OnlySearchLabel=false){
var language = "zh-CN";
if(!isZh){
language="en-US";
}
$.ajax({
type:"GET",
url:"/all/dosearch/",
headers: {
authorization:token,
},
dataType:"json",
data:{
'TableName': tableName,
'Key': keyword,
'Page': page,
'PageSize': pageSize,
'OnlyReturnNum':onlyReturnNum,
'SortBy':sortBy,
'OnlySearchLabel':OnlySearchLabel,
'Ascending':currentSearchAscending,
'WebTotal':totalNum,
'PrivateTotal':privateTotal,
'language':language
},
async:true,
success:function(json){
console.log("tableName=" + tableName);
console.log(json);
displayResult(tableName,page,json,onlyReturnNum,keyword);
},
error:function(response) {
console.log(response);
}
});
}

function displayResult(tableName,page,jsonResult,onlyReturnNum,keyword){
if(tableName == "repository") {
displayRepoResult(page,jsonResult,onlyReturnNum,keyword);
} else if (tableName == "issue") {
displayIssueResult(page,jsonResult,onlyReturnNum,keyword);
} else if (tableName == "user") {
displayUserResult(page,jsonResult,onlyReturnNum,keyword);
} else if (tableName == "org") {
displayOrgResult(page,jsonResult,onlyReturnNum,keyword);
} else if (tableName == "dataset") {
displayDataSetResult(page,jsonResult,onlyReturnNum,keyword);
} else if (tableName == "pr") {
displayPrResult(page,jsonResult,onlyReturnNum,keyword);
}
if(!onlyReturnNum){
console.log("set total num." + tableName);
totalPage =Math.ceil(jsonResult.Total/pageSize);
totalNum = jsonResult.Total;
privateTotal = jsonResult.PrivateTotal;
setPage(page);
}
}

function displayPrResult(page,jsonResult,onlyReturnNum,keyword){
var data = jsonResult.Result;
var total = jsonResult.Total;
$('#pr_total').text(total);
if(!onlyReturnNum){
setActivate("pr_item");
//$('#keyword_desc').text(keyword);
//$('#obj_desc').text(getLabel(isZh,"search_pr"));
//$('#child_total').text(total);
$('#find_title').html(getLabel(isZh,"find_title").replace('{keyword}',keyword).replace('{tablename}',getLabel(isZh,"search_pr")).replace('{total}',total));

setIssueOrPrInnerHtml(data,"pulls");
}
}

var categoryDesc={
"computer_vision":"计算机视觉",
"natural_language_processing":"自然语言处理",
"speech_processing":"语音处理",
"computer_vision_natural_language_processing":"计算机视觉、自然语言处理"
};

var categoryENDesc={
"computer_vision":"computer vision",
"natural_language_processing":"natural language processing",
"speech_processing":"speech processing",
"computer_vision_natural_language_processing":"computer vision and natural language processing"
};

var taskDesc={
"machine_translation":"机器翻译",
"question_answering_system":"问答系统",
"information_retrieval":"信息检索",
"knowledge_graph":"知识图谱",
"text_annotation":"文本标注",
"text_categorization":"文本分类",
"emotion_analysis":"情感分析",
"language_modeling":"语言建模",
"speech_recognition":"语音识别",
"automatic_digest":"自动文摘",
"information_extraction":"信息抽取",
"description_generation":"说明生成",
"image_classification":"图像分类",
"face_recognition":"人脸识别",
"image_search":"图像搜索",
"target_detection":"目标检测",
"image_description_generation":"图像描述生成",
"vehicle_license_plate_recognition":"车辆车牌识别",
"medical_image_analysis":"医学图像分析",
"unmanned":"无人驾驶",
"unmanned_security":"无人安防",
"drone":"无人机",
"vr_ar":"VR/AR",
"2_d_vision":"2-D视觉",
"2_5_d_vision":"2.5-D视觉",
"3_d_reconstruction":"3D重构",
"image_processing":"图像处理",
"video_processing":"视频处理",
"visual_input_system":"视觉输入系统",
"speech_coding":"语音编码",
"speech_enhancement":"语音增强",
"speech_recognition":"语音识别",
"speech_synthesis":"语音合成"
};

var taskENDesc={
"machine_translation":"machine translation",
"question_answering_system":"question answering system",
"information_retrieval":"information retrieval",
"knowledge_graph":"knowledge graph",
"text_annotation":"text annotation",
"text_categorization":"text categorization",
"emotion_analysis":"emotion analysis",
"language_modeling":"language modeling",
"speech_recognition":"speech recognition",
"automatic_digest":"automatic digest",
"information_extraction":"information extraction",
"description_generation":"description generation",
"image_classification":"image classification",
"face_recognition":"face recognition",
"image_search":"image search",
"target_detection":"target detection",
"image_description_generation":"image description generation",
"vehicle_license_plate_recognition":"vehicle license plate recognition",
"medical_image_analysis":"medical image analysis",
"unmanned":"unmanned",
"unmanned_security":"unmanned security",
"drone":"drone",
"vr_ar":"VR/AR",
"2_d_vision":"2.D vision",
"2.5_d_vision":"2.5D vision",
"3_d_reconstruction":"3Dreconstruction",
"image_processing":"image processing",
"video_processing":"video processing",
"visual_input_system":"visual input system",
"speech_coding":"speech coding",
"speech_enhancement":"speech enhancement",
"speech_recognition":"speech recognition",
"speech_synthesis":"speech synthesis"
};

function getCategoryDesc(isZh,key){
var re = key;
if(isZh){
re = categoryDesc[key];
}else{
re = categoryENDesc[key];
}
if(isEmpty(re)){
return key;
}
return re;
}

function getTaskDesc(isZh,key){
var re = key;
if(isZh){
re = taskDesc[key];
}else{
re = taskENDesc[key];
}
if(isEmpty(re)){
return key;
}
return re;
}

function getActiveItem(sort_type){
console.log("currentSearchSortBy=" + currentSearchSortBy + " sort_type=" + sortBy[sort_type]);
if(currentSearchSortBy == sortBy[sort_type] && currentSearchAscending == sortAscending[sort_type]){
return "active ";
}else{
return "";
}
}

function displayDataSetResult(page,jsonResult,onlyReturnNum,keyword){
var data = jsonResult.Result;
var total = jsonResult.Total;
$('#dataset_total').text(total);
if(!onlyReturnNum){
setActivate("dataset_item");
//$('#keyword_desc').text(keyword);
//$('#obj_desc').text(getLabel(isZh,"search_dataset"));
//$('#child_total').text(total);
$('#find_title').html(getLabel(isZh,"find_title").replace('{keyword}',keyword).replace('{tablename}',getLabel(isZh,"search_dataset")).replace('{total}',total));

var sortHtml = "";
sortHtml +="<a class=\"" + getActiveItem(50) + "item\" href=\"javascript:searchItem(5,50);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_matched") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(51) + "item\" href=\"javascript:searchItem(5,51);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_matched_download") + "</a>";
document.getElementById("sort_type").innerHTML=sortHtml;

var html = "";
var currentTime = new Date().getTime();
for(var i = 0; i < data.length;i++){
var recordMap = data[i];
html += "<div class=\"item\">";
html += " <div class=\"content\">";
html += " <div class=\"ui right metas\">" ;
if(!isEmpty(recordMap["category"])){
html += " <span class=\"text grey\"><svg class=\"svg octicon-tasklist\" width=\"16\" height=\"16\" aria-hidden=\"true\"><use xlink:href=\"#octicon-tasklist\" /></svg> " + getCategoryDesc(isZh,recordMap["category"]) + "</span>";
}
if(!isEmpty(recordMap["task"])){
html += " <span class=\"text grey\"><svg class=\"svg octicon-tag\" width=\"16\" height=\"16\" aria-hidden=\"true\"><use xlink:href=\"#octicon-tag\" /></svg>" + getTaskDesc(isZh,recordMap["task"]) + "</span>";
}
html += " <span class=\"text grey\"><i class=\"ri-fire-line\"></i> " +recordMap["download_times"] + "</span> ";
html +=" </div>";
html += " <div class=\"ui header\">";
html += " <a class=\"name\" href=\"/" +recordMap["repoUrl"] +"/datasets\" target=\"_blank\">" + recordMap["title"] + "</a>";
html +=" <span class=\"middle\"><svg class=\"svg octicon-repo-clone\" width=\"16\" height=\"16\" aria-hidden=\"true\"><use xlink:href=\"#octicon-repo-clone\"></use></svg></span>";
html +=" </div>";
html += " <div class=\"description\">";
html += " <p class=\"has-emoji\"> " + recordMap["description"] + "</p>";
if(!isEmpty(recordMap["file_name"])){
html += " <p class=\"has-emoji\"> " + recordMap["file_name"] + "</p>";
}
html +=" <p class=\"time\">";
html +=" <span class=\"am-ml-10\"></span> "+ getLabel(isZh,"search_lasted_update") + " " + recordMap["updated_html"];
html +=" </p>";
html +=" </div>";
html +=" </div>";
html +="</div>";
}
document.getElementById("child_search_item").innerHTML=html;
}
}

function displayOrgResult(page,jsonResult,onlyReturnNum,keyword){
var data = jsonResult.Result;
var total = jsonResult.Total;
$('#org_total').text(total);
if(!onlyReturnNum){
setActivate("org_item");
//$('#keyword_desc').text(keyword);
//$('#obj_desc').text(getLabel(isZh,"search_org"));
//$('#child_total').text(total);
$('#find_title').html(getLabel(isZh,"find_title").replace('{keyword}',keyword).replace('{tablename}',getLabel(isZh,"search_org")).replace('{total}',total));

var sortHtml = "";
sortHtml +="<a class=\"" + getActiveItem(40) + "item\" href=\"javascript:searchItem(4,40);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_matched") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(41) + "item\" href=\"javascript:searchItem(4,41);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_letter_asc") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(42) + "item\" href=\"javascript:searchItem(4,42);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_2\">"+ getLabel(isZh,"search_letter_desc") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(43) + "item\" href=\"javascript:searchItem(4,43);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_2\">"+ getLabel(isZh,"search_lasted_create") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(44) + "item\" href=\"javascript:searchItem(4,44);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_2\">"+ getLabel(isZh,"search_early_create") + "</a>";
document.getElementById("sort_type").innerHTML=sortHtml;

var html = "";
var currentTime = new Date().getTime();
for(var i = 0; i < data.length;i++){
var recordMap = data[i];
html += "<div class=\"item members\">";
html += "<img class=\"ui avatar image\" src=\"" + recordMap["avatar"] + "\"></img>";
html += " <div class=\"content\">";
html += " <div class=\"ui header\">";
html += " <a class=\"name\" href=\"/" + recordMap["real_name"] +"\" target=\"_blank\">" + recordMap["name"] + "&nbsp;&nbsp;" + recordMap["full_name"] + "</a>";
html +=" </div>";
html += " <div class=\"description\">";
html += " <p class=\"has-emoji\"> " + recordMap["description"] + "</p>";
html +=" <p class=\"has-emoji\">";
if(!isEmpty(recordMap["location"]) && recordMap["location"] != "null"){
html +=" <i class=\"ri-map-pin-2-line\"></i> " + recordMap["location"];
}
html +=" <span class=\"am-ml-10\"></span>";
if(!isEmpty(recordMap["website"]) && recordMap["website"] != "null"){
html +=" <i class=\"ri-links-line\"></i>" + "<a href=\""+ recordMap["website"] + "\" target=\"_blank\">" + recordMap["website"] + "</a>";
}
html +=" <i class=\"ri-time-line am-ml-10\"></i> "+ getLabel(isZh,"search_add_by") + " ";
html += recordMap["add_time"]
html +=" </p>";
html +=" </div>";
html +=" </div>";
html +="</div>";
}
document.getElementById("child_search_item").innerHTML=html;
}
}
var monthDisplay=new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Spt","Oct","Nov","Dec");
function displayUserResult(page,jsonResult,onlyReturnNum,keyword){
var data = jsonResult.Result;
var total = jsonResult.Total;
$('#user_total').text(total);
if(!onlyReturnNum){
setActivate("user_item");
//$('#keyword_desc').text(keyword);
//$('#obj_desc').text(getLabel(isZh,"search_user"));
//$('#child_total').text(total);

$('#find_title').html(getLabel(isZh,"find_title").replace('{keyword}',keyword).replace('{tablename}',getLabel(isZh,"search_user")).replace('{total}',total));

var sortHtml = "";//equal user sort by
sortHtml +="<a class=\"" + getActiveItem(30) + "item\" href=\"javascript:searchItem(3,30);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_matched") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(31) + "item\" href=\"javascript:searchItem(3,31);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_letter_asc") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(32) + "item\" href=\"javascript:searchItem(3,32);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_2\">"+ getLabel(isZh,"search_letter_desc") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(33) + "item\" href=\"javascript:searchItem(3,33);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_2\">"+ getLabel(isZh,"search_lasted_create") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(34) + "item\" href=\"javascript:searchItem(3,34);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_2\">"+ getLabel(isZh,"search_early_create") + "</a>";

document.getElementById("sort_type").innerHTML=sortHtml;

var html = "";
var currentTime = new Date().getTime();
for(var i = 0; i < data.length;i++){
var recordMap = data[i];
html += "<div class=\"item members\">";
html += "<img class=\"ui avatar image\" src=\"" + recordMap["avatar"] + "\"></img>";
html += " <div class=\"content\">";
html += " <div class=\"ui header\">";
html += " <a class=\"name\" href=\"/" + recordMap["real_name"] +"\" target=\"_blank\">" + recordMap["name"] + "&nbsp;&nbsp;" + recordMap["full_name"] + "</a>";
html +=" </div>";
html += " <div class=\"description\">";
html += " <p class=\"has-emoji\"> " + recordMap["description"] + "</p>";
html +=" <p class=\"has-emoji\">";
if(!isEmpty(recordMap["email"]) && recordMap["email"] != "null"){
html +=" <i class=\"ri-mail-line\"></i>&nbsp;<a href=\"mailto:" + recordMap["email"] + "\" rel=\"nofollow\">" + recordMap["email"] + "</a>";
}
html +=" <i class=\"ri-time-line am-ml-10\"></i> "+ getLabel(isZh,"search_add_by") + " ";
html += recordMap["add_time"]
html +=" </p>";
html +=" </div>";
html +=" </div>";
html +="</div>";
}
document.getElementById("child_search_item").innerHTML=html;
}
}

function setIssueOrPrInnerHtml(data,path){
var sortHtml = "";
if(path =="issues"){
sortHtml +="<a class=\"" + getActiveItem(20) + "item\" href=\"javascript:searchItem(2,20);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_matched") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(21) + "item\" href=\"javascript:searchItem(2,21);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_lasted") + "</a>";
}else{
sortHtml +="<a class=\"" + getActiveItem(60) + "item\" href=\"javascript:searchItem(6,60);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_matched") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(61) + "item\" href=\"javascript:searchItem(6,61);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_lasted") + "</a>";
}
document.getElementById("sort_type").innerHTML=sortHtml;

var html = "";
var currentTime = new Date().getTime();
for(var i = 0; i < data.length;i++){
var recordMap = data[i];
html += "<div class=\"item\">";
html += " <div class=\"content\">";
html += " <div class=\"ui header\">";
html += " <a class=\"name\" href=\"/" + recordMap["repoUrl"] +"/" + path + "/" + recordMap["index"] + "\" target=\"_blank\">" + recordMap["name"] + "</a>";
html +=" </div>";
html += " <div class=\"description\">";
html += " <p class=\"has-emoji\"> " + recordMap["content"] + "</p>";
html +=" <p class=\"time\">";
html +=" <i class=\"ri-code-box-line\"></i>";
html +=" <a class=\"am-text grey\" href=\"/" + recordMap["repoUrl"] +"/" + path + "/" + recordMap["index"] + "\" target=\"_blank\"> " + addBlank(recordMap["repoUrl"]) +" #" + recordMap["index"] + "</a>&nbsp;&nbsp;&nbsp;&nbsp;";
html +=" <i class=\"ri-information-line am-ml-10\"></i>&nbsp;";
if(recordMap["is_closed"] != null && (!(recordMap["is_closed"]) || recordMap["is_closed"]=="f")){
html += getLabel(isZh,"search_open");
}else{
html += getLabel(isZh,"search_closed");
}
html +=" &nbsp;&nbsp;&nbsp;&nbsp;<i class=\"ri-message-2-line am-ml-10\"></i>&nbsp;" + recordMap["num_comments"];
html +=" <span class=\"am-ml-10\">&nbsp;&nbsp;</span>&nbsp;&nbsp;"+ getLabel(isZh,"search_lasted_update") + " "+ recordMap["updated_html"];
html +=" </p>";
html +=" </div>";
html +=" </div>";
html +="</div>";
}
document.getElementById("child_search_item").innerHTML=html;
}

function addBlank(url){
if(url == null){
return url;
}
var tmps = url.split("/");
if(tmps.length == 2){
return tmps[0] + " / " + tmps[1];
}
return url;
}

function displayIssueResult(page,jsonResult,onlyReturnNum,keyword){
var data = jsonResult.Result;
var total = jsonResult.Total;
$('#issue_total').text(total);
if(!onlyReturnNum){
setActivate("issue_item");
//$('#keyword_desc').text(keyword);
//$('#obj_desc').text(getLabel(isZh,"search_issue"));
//$('#child_total').text(total);
$('#find_title').html(getLabel(isZh,"find_title").replace('{keyword}',keyword).replace('{tablename}',getLabel(isZh,"search_issue")).replace('{total}',total));

setIssueOrPrInnerHtml(data,"issues");
}
}

function setActivate(name){
$('#repo_item').removeClass("active");
$('#user_item').removeClass("active");
$('#issue_item').removeClass("active");
$('#dataset_item').removeClass("active");
$('#org_item').removeClass("active");
$('#pr_item').removeClass("active");
if(name==null){
return;
}
var tmp = "#" + name;
$(tmp).addClass("active");
}

function displayRepoResult(page,jsonResult,onlyReturnNum,keyword){
var data = jsonResult.Result;
var total = jsonResult.Total;
$('#repo_total').text(total);

if(!onlyReturnNum){
setActivate("repo_item");
// $('#keyword_desc').text(keyword);
//$('#obj_desc').text(getLabel(isZh,"search_repo"));
//$('#child_total').text(total);
$('#find_title').html(getLabel(isZh,"find_title").replace('{keyword}',keyword).replace('{tablename}',getLabel(isZh,"search_repo")).replace('{total}',total));

var sortHtml = "";
sortHtml +="<a class=\"" + getActiveItem(10) + "item\" href=\"javascript:searchItem(1,10);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_matched") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(11) + "item\" href=\"javascript:searchItem(1,11);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_lasted") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(12) + "item\" href=\"javascript:searchItem(1,12);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_watched") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(13) + "item\" href=\"javascript:searchItem(1,13);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_star") + "</a>";
sortHtml +="<a class=\"" + getActiveItem(14) + "item\" href=\"javascript:searchItem(1,14);\" tabindex=\"-1\" role=\"menuitem\" id=\"menuitem_1\">"+ getLabel(isZh,"search_fork") + "</a>";

document.getElementById("sort_type").innerHTML=sortHtml;

var html = "";
var currentTime = new Date().getTime();
for(var i = 0; i < data.length;i++){
var recordMap = data[i];
html += "<div class=\"item\">";
if(!isEmpty(recordMap['avatar'])){
html += "<img class=\"ui avatar image\" src=\"" + recordMap['avatar'] + "\">";
}
html += " <div class=\"content\">";
html += " <div class=\"ui header\">";
html += " <a class=\"name\" href=\"/" + recordMap["owner_name"] + "/" + recordMap["real_name"] +"\" target=\"_blank\"> <span>" + recordMap["owner_name"] +"</span> <span>/</span> <strong>" + recordMap["alias"] + "</strong></a>";
if(recordMap["is_private"]){
html +=" <span class=\"middle text gold\"><svg class=\"svg octicon-lock\" width=\"16\" height=\"16\" aria-hidden=\"true\"><use xlink:href=\"#octicon-lock\" /></svg></span>";
}
html +=" </div>";
html += " <div class=\"description\">";
html += " <p class=\"has-emoji\"> " + recordMap["description"] + "</p>";
html += " <div class=\"ui tags\">";
if(!isEmpty(recordMap["topics"]) && recordMap["topics"] !="null"){
for(var j = 0; j < recordMap["topics"].length;j++){
//function searchLabel(tableName,keyword,sortBy="",ascending=false)
html +=" <a href=\"javascript:searchLabel('repository','" + recordMap["topics"][j] + "','updated_unix.keyword',false);\" ><div class=\"ui small label topic\">"+ recordMap["hightTopics"][j] + "</div></a>";
}
}
html +=" </div>";
html +=" <p class=\"time\">";
html +=" <i class=\"icon fa-eye outline\"></i>&nbsp;" + recordMap["num_watches"] + "&nbsp;&nbsp;<i class=\"icon star outline\"></i>&nbsp;" + recordMap["num_stars"] + "&nbsp;&nbsp;<i class=\"icon code branch\"></i>&nbsp;" + recordMap["num_forks"] +"&nbsp;&nbsp;";
html +="&nbsp;&nbsp;&nbsp;&nbsp;"+ getLabel(isZh,"search_lasted_update") + " " + recordMap["updated_html"];
if(!isEmpty(recordMap["lang"])){
var lang = recordMap["lang"]
var tmpLang = recordMap["lang"].split(",");
if(tmpLang.length>0){
lang = tmpLang[0]
}
var backColor = "#3572A5";
if(LanguagesColor[lang] != null){
backColor = LanguagesColor[lang];
}
html +=" <span class=\"text grey am-ml-10\"><i class=\"color-icon\" style=\"background-color: "+ backColor + "\"></i>&nbsp;" + lang + "</span>";
}
html +=" </p>";
html +=" </div>";
html +=" </div>";
html +="</div>";
}

document.getElementById("child_search_item").innerHTML=html;
}
}

function getTime(UpdatedUnix,currentTime){
UpdatedUnix = UpdatedUnix;
currentTime = currentTime / 1000;
var timeEscSecond = currentTime - UpdatedUnix;
if( timeEscSecond < 0){
timeEscSecond = 1;
}
console.log("currentTime=" + currentTime + " updateUnix=" + UpdatedUnix);
var hours= Math.floor(timeEscSecond / 3600);
//计算相差分钟数
var leave2 = Math.floor(timeEscSecond % (3600)); //计算小时数后剩余的秒数
var minutes= Math.floor(leave2 / 60);//计算相差分钟数

var leave3=Math.floor(leave2 % 60); //计算分钟数后剩余的秒数
var seconds= leave3;

if(hours == 0 && minutes == 0){
return seconds + getRepoOrOrg(6,isZh);
}else{
if(hours > 0){
if(hours >= 24){
var days = Math.ceil(hours/24)
if (days >= 30 && days <365){
return Math.ceil(days/30) + getRepoOrOrg(8,isZh);
}else if(days >= 365){
return Math.ceil(days/365) + getRepoOrOrg(9,isZh);
}
return Math.ceil(hours/24) + getRepoOrOrg(7,isZh);
}else{
return hours + getRepoOrOrg(4,isZh);
}
}else{
return minutes + getRepoOrOrg(5,isZh);
}
}
}

function getRepoOrOrg(key,isZhLang){
if(isZhLang){
return repoAndOrgZH[key];
}else{
return repoAndOrgEN[key];
}
}

var repoAndOrgZH={
"1":"项目",
"2":"成员",
"3":"团队",
"4":"小时前",
"5":"分钟前",
"6":"秒前",
"7":"天前",
"8":"个月前",
"9":"年前"
};

var repoAndOrgEN={
"1":"repository",
"2":"Members ",
"3":"Teams",
"4":" hours ago",
"5":" minutes ago",
"6":" seconds ago",
"7":" day ago",
"8":" month ago",
"9":" year ago"
};




function page(current){
currentPage=current;
doSearch(currentSearchTableName,currentSearchKeyword,current,pageSize,false,currentSearchSortBy,OnlySearchLabel);
}
function nextPage(){
currentPage = currentPage+1;
console.log("currentPage=" + currentPage);
if(currentPage >= endIndex){
startIndex=startIndex+1;
endIndex = endIndex +1;
}
page(currentPage);
}
function prePage(){
console.log("currentPage=" + currentPage);
if(currentPage > 1){
currentPage = currentPage-1;
if(currentPage <= startIndex && startIndex > 1){
startIndex = startIndex -1;
endIndex = endIndex - 1;
}
console.log("currentPage=" + (currentPage));
page(currentPage);
}
}

function getXPosition(e){
var x=e.offsetLeft;
while(e=e.offsetParent)
{
x+=e.offsetLeft;
}
return x+20;//-260防止屏幕超出
}
//获取y坐标
function getYPosition(e){
var y=e.offsetTop;
while(e=e.offsetParent)
{
y+=e.offsetTop;
}
return y+20;//80为input高度
}


function goPage(event){
var inputpage = document.getElementById("inputpage_div")
var left = getXPosition(event.target);
var top = getYPosition(event.target);
var goNum = $('#inputpage').val();
if (goNum<=0){
showTip(getLabel(isZh,"search_input_large_0"),"warning",left+5,top);
}
else if(goNum<=totalPage){
page(goNum);
}
else{
showTip(getLabel(isZh,"search_input_maxed"),"warning",left+5,top);
}
}

function showTip(tip, type,left,top) {
var $tip = $('#tipmsg');
var tipmsg = document.getElementById("tipmsg")
var style="z-index:10024;top:" + top + "px;left:" + left + "px;position:absolute;width:200px;height:60px;vertical-align:middle;";
console.log(style);
tipmsg.style = style;
var html ="<p>" + tip + "</p>"
$tip.stop(true).prop('class', 'alert alert-' + type).html(html).fadeIn(500).delay(2000).fadeOut(500);
}

function setPage(currentPage){
console.log("totalPage=" + totalPage);
var html ="";
console.log("currentPage=" + currentPage);
console.log("privateTotal=" + privateTotal);
// if(totalPage==0){
// return;
// }
html += "<span class=\"item\">" + getLabel(isZh,"search_input_total") + " " + totalNum + " " + getLabel(isZh,"search_srtip") + "</span>"
if(currentPage > 1){
html += "<a class=\"item navigation\" href=\"javascript:page(1)\"><span class=\"navigation_label\">" + getLabel(isZh,"search_home_page") + "</span></a>";
html += "<a class=\"item navigation\" href=\"javascript:prePage()\"><i class=\"left arrow icon\"></i></a>";
}else{
html += "<a class=\"disabled item navigation\" href=\"javascript:page(1)\"><span class=\"navigation_label\">" + getLabel(isZh,"search_home_page") + "</span></a>";
html += "<a class=\"disabled item navigation\" href=\"javascript:prePage()\"><i class=\"left arrow icon\"></i></a>";
}
for(var i=startIndex; i <= endIndex; i++){
var page_i = i;
if(page_i > totalPage){
break;
}
if( page_i == currentPage){
html += "<a id=\"page_" + page_i+ "\" class=\"active item\" href=\"javascript:page(" + page_i +")\">" + page_i + "</a>";
}else{
html += "<a id=\"page_" + page_i+ "\" class=\"item\" href=\"javascript:page(" + page_i +")\">" + page_i + "</a>";
}
}

if(currentPage >=totalPage){
html += "<a class=\"disabled item navigation\" href=\"javascript:nextPage()\"><i class=\"icon right arrow\"></i></a>";
html += "<a class=\"disabled item navigation\" href=\"javascript:page(" + totalPage + ")\"><span class=\"navigation_label\">" + getLabel(isZh,"search_last_page") + "</span></a>";
}else{
html += "<a class=\"item navigation\" href=\"javascript:nextPage()\"><i class=\"icon right arrow\"></i></a>";
html += "<a class=\"item navigation\" href=\"javascript:page(" + totalPage + ")\"><span class=\"navigation_label\">" + getLabel(isZh,"search_last_page") + "</span></a>";
}

html +="<div class=\"item\"> " + getLabel(isZh,"search_go_to") + "<div id=\"inputpage_div\" class=\"ui input\"><input id=\"inputpage\" type=\"text\"></div>" + getLabel(isZh,"search_go_page") + "</div>";
console.log("html=" + html)
document.getElementById("page_menu").innerHTML=html;
$('#inputpage').on('keypress',function(event){
if(event.keyCode == 13){
goPage(event);
}
});
}

$('#keyword_input').on('keypress',function(event){
if(event.keyCode == 13){
search();
}
});





var LanguagesColor = {
"1C Enterprise": "#814CCC",
"ABAP": "#E8274B",
"AGS Script": "#B9D9FF",
"AMPL": "#E6EFBB",
"ANTLR": "#9DC3FF",
"API Blueprint": "#2ACCA8",
"APL": "#5A8164",
"ASP": "#6a40fd",
"ATS": "#1ac620",
"ActionScript": "#882B0F",
"Ada": "#02f88c",
"Agda": "#315665",
"Alloy": "#64C800",
"AngelScript": "#C7D7DC",
"AppleScript": "#101F1F",
"Arc": "#aa2afe",
"AspectJ": "#a957b0",
"Assembly": "#6E4C13",
"Asymptote": "#4a0c0c",
"AutoHotkey": "#6594b9",
"AutoIt": "#1C3552",
"Ballerina": "#FF5000",
"Batchfile": "#C1F12E",
"BlitzMax": "#cd6400",
"Boo": "#d4bec1",
"Brainfuck": "#2F2530",
"C": "#555555",
"C#": "#178600",
"C++": "#f34b7d",
"CSS": "#563d7c",
"Ceylon": "#dfa535",
"Chapel": "#8dc63f",
"Cirru": "#ccccff",
"Clarion": "#db901e",
"Clean": "#3F85AF",
"Click": "#E4E6F3",
"Clojure": "#db5855",
"CoffeeScript": "#244776",
"ColdFusion": "#ed2cd6",
"Common Lisp": "#3fb68b",
"Common Workflow Language": "#B5314C",
"Component Pascal": "#B0CE4E",
"Crystal": "#000100",
"Cuda": "#3A4E3A",
"D": "#ba595e",
"DM": "#447265",
"Dart": "#00B4AB",
"DataWeave": "#003a52",
"Dhall": "#dfafff",
"Dockerfile": "#384d54",
"Dogescript": "#cca760",
"Dylan": "#6c616e",
"E": "#ccce35",
"ECL": "#8a1267",
"EQ": "#a78649",
"Eiffel": "#946d57",
"Elixir": "#6e4a7e",
"Elm": "#60B5CC",
"Emacs Lisp": "#c065db",
"EmberScript": "#FFF4F3",
"Erlang": "#B83998",
"F#": "#b845fc",
"F*": "#572e30",
"FLUX": "#88ccff",
"Factor": "#636746",
"Fancy": "#7b9db4",
"Fantom": "#14253c",
"Faust": "#c37240",
"Forth": "#341708",
"Fortran": "#4d41b1",
"FreeMarker": "#0050b2",
"Frege": "#00cafe",
"G-code": "#D08CF2",
"GAML": "#FFC766",
"GDScript": "#355570",
"Game Maker Language": "#71b417",
"Genie": "#fb855d",
"Gherkin": "#5B2063",
"Glyph": "#c1ac7f",
"Gnuplot": "#f0a9f0",
"Go": "#00ADD8",
"Golo": "#88562A",
"Gosu": "#82937f",
"Grammatical Framework": "#79aa7a",
"Groovy": "#e69f56",
"HTML": "#e34c26",
"Hack": "#878787",
"Harbour": "#0e60e3",
"Haskell": "#5e5086",
"Haxe": "#df7900",
"HiveQL": "#dce200",
"HolyC": "#ffefaf",
"Hy": "#7790B2",
"IDL": "#a3522f",
"IGOR Pro": "#0000cc",
"Idris": "#b30000",
"Io": "#a9188d",
"Ioke": "#078193",
"Isabelle": "#FEFE00",
"J": "#9EEDFF",
"JSONiq": "#40d47e",
"Java": "#b07219",
"JavaScript": "#f1e05a",
"Jolie": "#843179",
"Jsonnet": "#0064bd",
"Julia": "#a270ba",
"Jupyter Notebook": "#DA5B0B",
"KRL": "#28430A",
"Kotlin": "#F18E33",
"LFE": "#4C3023",
"LLVM": "#185619",
"LOLCODE": "#cc9900",
"LSL": "#3d9970",
"Lasso": "#999999",
"Lex": "#DBCA00",
"LiveScript": "#499886",
"LookML": "#652B81",
"Lua": "#000080",
"MATLAB": "#e16737",
"MAXScript": "#00a6a6",
"MLIR": "#5EC8DB",
"MQL4": "#62A8D6",
"MQL5": "#4A76B8",
"MTML": "#b7e1f4",
"Makefile": "#427819",
"Mask": "#f97732",
"Max": "#c4a79c",
"Mercury": "#ff2b2b",
"Meson": "#007800",
"Metal": "#8f14e9",
"Mirah": "#c7a938",
"Modula-3": "#223388",
"NCL": "#28431f",
"Nearley": "#990000",
"Nemerle": "#3d3c6e",
"NetLinx": "#0aa0ff",
"NetLinx+ERB": "#747faa",
"NetLogo": "#ff6375",
"NewLisp": "#87AED7",
"Nextflow": "#3ac486",
"Nim": "#37775b",
"Nit": "#009917",
"Nix": "#7e7eff",
"Nu": "#c9df40",
"OCaml": "#3be133",
"ObjectScript": "#424893",
"Objective-C": "#438eff",
"Objective-C++": "#6866fb",
"Objective-J": "#ff0c5a",
"Odin": "#60AFFE",
"Omgrofl": "#cabbff",
"Opal": "#f7ede0",
"OpenQASM": "#AA70FF",
"Oxygene": "#cdd0e3",
"Oz": "#fab738",
"P4": "#7055b5",
"PHP": "#4F5D95",
"PLSQL": "#dad8d8",
"Pan": "#cc0000",
"Papyrus": "#6600cc",
"Parrot": "#f3ca0a",
"Pascal": "#E3F171",
"Pawn": "#dbb284",
"Pep8": "#C76F5B",
"Perl": "#0298c3",
"PigLatin": "#fcd7de",
"Pike": "#005390",
"PogoScript": "#d80074",
"PostScript": "#da291c",
"PowerBuilder": "#8f0f8d",
"PowerShell": "#012456",
"Processing": "#0096D8",
"Prolog": "#74283c",
"Propeller Spin": "#7fa2a7",
"Puppet": "#302B6D",
"PureBasic": "#5a6986",
"PureScript": "#1D222D",
"Python": "#3572A5",
"QML": "#44a51c",
"Quake": "#882233",
"R": "#198CE7",
"RAML": "#77d9fb",
"RUNOFF": "#665a4e",
"Racket": "#3c5caa",
"Ragel": "#9d5200",
"Raku": "#0000fb",
"Rascal": "#fffaa0",
"Reason": "#ff5847",
"Rebol": "#358a5b",
"Red": "#f50000",
"Ren'Py": "#ff7f7f",
"Ring": "#2D54CB",
"Riot": "#A71E49",
"Roff": "#ecdebe",
"Rouge": "#cc0088",
"Ruby": "#701516",
"Rust": "#dea584",
"SAS": "#B34936",
"SQF": "#3F3F3F",
"SRecode Template": "#348a34",
"SaltStack": "#646464",
"Scala": "#c22d40",
"Scheme": "#1e4aec",
"Self": "#0579aa",
"Shell": "#89e051",
"Shen": "#120F14",
"Slash": "#007eff",
"Slice": "#003fa2",
"SmPL": "#c94949",
"Smalltalk": "#596706",
"Solidity": "#AA6746",
"SourcePawn": "#5c7611",
"Squirrel": "#800000",
"Stan": "#b2011d",
"Standard ML": "#dc566d",
"Starlark": "#76d275",
"SuperCollider": "#46390b",
"Swift": "#ffac45",
"SystemVerilog": "#DAE1C2",
"TI Program": "#A0AA87",
"Tcl": "#e4cc98",
"TeX": "#3D6117",
"Terra": "#00004c",
"Turing": "#cf142b",
"TypeScript": "#2b7489",
"UnrealScript": "#a54c4d",
"V": "#5d87bd",
"VBA": "#867db1",
"VBScript": "#15dcdc",
"VCL": "#148AA8",
"VHDL": "#adb2cb",
"Vala": "#fbe5cd",
"Verilog": "#b2b7f8",
"Vim script": "#199f4b",
"Visual Basic .NET": "#945db7",
"Volt": "#1F1F1F",
"Vue": "#2c3e50",
"WebAssembly": "#04133b",
"Wollok": "#a23738",
"X10": "#4B6BEF",
"XC": "#99DA07",
"XQuery": "#5232e7",
"XSLT": "#EB8CEB",
"YARA": "#220000",
"YASnippet": "#32AB90",
"Yacc": "#4B6C4B",
"ZAP": "#0d665e",
"ZIL": "#dc75e5",
"ZenScript": "#00BCD1",
"Zephir": "#118f9e",
"Zig": "#ec915c",
"eC": "#913960",
"mIRC Script": "#926059",
"mcfunction": "#E22837",
"nesC": "#94B0C7",
"ooc": "#b0b77e",
"q": "#0040cd",
"sed": "#64b970",
"wdl": "#42f1f4",
"wisp": "#7582D1",
"xBase": "#403a40",
}

function getLabel(isZh,key){
if(isZh){
return zhCN[key]
}else{
return esUN[key]
}
}

var zhCN={
"search":"搜索",
"search_repo":"项目",
"search_dataset":"数据集",
"search_issue":"任务",
"search_pr":"合并请求",
"search_user":"用户",
"search_org":"组织",
"search_finded":"找到",
"search_matched":"最佳匹配",
"search_matched_download":"下载次数",
"search_lasted_update":"最后更新于",
"search_letter_asc":"字母顺序排序",
"search_letter_desc":"字母逆序排序",
"search_lasted_create":"最近创建",
"search_early_create":"最早创建",
"search_add_by":"加入于",
"search_lasted":"最近更新",
"search_open":"开启中",
"search_closed":"已关闭",
"search_watched":"关注数",
"search_star":"点赞数",
"search_fork":"Fork数",
"search_input_large_0":"请输入大于0的数值。",
"search_input_maxed":"不能超出总页数。",
"search_input_total":"共",
"search_srtip":"条",
"search_home_page":"首页",
"search_last_page":"末页",
"search_go_to":"前往",
"search_go_page":"页",
"find_title":"“<strong class=\"highlight\" id=\"keyword_desc\">{keyword}</strong>”相关{tablename}约为{total}个",
"search_empty":"<strong>请输入任意关键字开始搜索。</strong>"
}
var esUN={
"search":"Search",
"search_repo":"Repository",
"search_dataset":"DataSet",
"search_issue":"Issue",
"search_pr":"Pull Request",
"search_user":"User",
"search_org":"Organization",
"search_finded":"Find",
"search_matched":"Best Match",
"search_matched_download":"Most downloads",
"search_lasted_update":"Updated ",
"search_letter_asc":"Alphabetically",
"search_letter_desc":"Reverse alphabetically",
"search_lasted_create":"Recently created",
"search_early_create":"First created",
"search_add_by":"Joined on",
"search_lasted":"Recently updated",
"search_open":"Open",
"search_closed":"Closed",
"search_watched":"Watches",
"search_star":"Stars",
"search_fork":"Forks",
"search_input_large_0":"Please enter a value greater than 0.",
"search_input_maxed":"Cannot exceed total pages.",
"search_input_total":"Total",
"search_srtip":"",
"search_home_page":"First",
"search_last_page":"Last",
"search_go_to":"Go",
"search_go_page":"Page",
"find_title":" {total} \"<strong class=\"highlight\" id=\"keyword_desc\">{keyword}</strong>\" related {tablename}",
"search_empty":"<strong>Please enter any keyword to start the search.</strong>"
}
initDiv(false);
document.onreadystatechange = function() {
if (document.readyState === "complete") {
var tmpSearchLabel = sessionStorage.getItem("searchLabel");
console.log("tmpSearchLabel=" + tmpSearchLabel);
if(tmpSearchLabel){
console.log("search label....");
sessionStorage.removeItem("searchLabel");
doSearchLabel(sessionStorage.getItem("tableName"),sessionStorage.getItem("keyword"),sessionStorage.getItem("sortBy"),sessionStorage.getItem("ascending"));
}else{
console.log("normal search....");
search();
}
}
}


+ 3
- 3
public/self/dataset_preview.js View File

@@ -620,10 +620,10 @@ function showfilelist(){
for (var i=0;i<labeltastresult.length;i++){
var fname = labeltastresult[i].pic_image_field.substring(labeltastresult[i].pic_image_field.lastIndexOf('/') + 1);
console.log(labeltastresult[i])
//console.log(labeltastresult[i])
if(labeltastresult[i].pic_image_field.length > 70){
var tmpIndex = labeltastresult[i].pic_image_field.indexOf("/",70);
console.log(tmpIndex)
//console.log(tmpIndex)
if(tmpIndex != -1){
fname = labeltastresult[i].pic_image_field.substring(tmpIndex + 1);
fname = fname.substring(fname.indexOf('/')+1);
@@ -679,7 +679,7 @@ function breadFiles(){
fname_full_path = tableData[fileindex].pic_image_field.substring(tmp_index + 1);
}
var fname_path = fname_full_path.split('/')
console.log(fname_path)
//console.log(fname_path)
// var filename_text = tableData[fileindex].pic_image_field.substring(tableData[fileindex].pic_image_field.lastIndexOf('/')+1)
var html_breadFile = ''
// var source_name = filename_title+'.zip'


+ 2
- 0
routers/init.go View File

@@ -71,6 +71,8 @@ func NewServices() {
log.Info("decompression.NewContext() succeed.")
labelmsg.Init()
log.Info("labelmsg.Init() succeed.")
InitESClient()
log.Info("ES Client succeed.")
}

// In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology


+ 3
- 0
routers/routes/routes.go View File

@@ -323,6 +323,9 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/action/notification", routers.ActionNotification)
m.Get("/recommend/org", routers.RecommendOrgFromPromote)
m.Get("/recommend/repo", routers.RecommendRepoFromPromote)
m.Post("/all/search/", routers.Search)
m.Get("/all/search/", routers.EmptySearch)
m.Get("/all/dosearch/", routers.SearchApi)
m.Get("/home/term", routers.HomeTerm)
m.Group("/explore", func() {
m.Get("", func(ctx *context.Context) {


+ 1190
- 0
routers/search.go View File

@@ -0,0 +1,1190 @@
package routers

import (
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"github.com/olivere/elastic/v7"
)

type SearchRes struct {
Total int64
Result []map[string]interface{}
PrivateTotal int64
}

var client *elastic.Client

func InitESClient() {
ESSearchUrl := setting.ESSearchURL
var err error
client, err = elastic.NewClient(elastic.SetSniff(false), elastic.SetURL(ESSearchUrl))
if err != nil {
log.Info("es init error.")
//panic(err)
}
}

func EmptySearch(ctx *context.Context) {
log.Info("search template.")
ctx.Data["Keyword"] = ""
ctx.HTML(200, "explore/search_new")
}

func Search(ctx *context.Context) {
log.Info("search template.")
keyword := strings.Trim(ctx.Query("q"), " ")
ctx.Data["Keyword"] = keyword
ctx.Data["SortType"] = "newest"
ctx.HTML(200, "explore/search_new")
}

func SearchApi(ctx *context.Context) {
TableName := ctx.Query("TableName")
Key := ctx.Query("Key")
Page := ctx.QueryInt("Page")
PageSize := ctx.QueryInt("PageSize")
OnlyReturnNum := ctx.QueryBool("OnlyReturnNum")
OnlySearchLabel := ctx.QueryBool("OnlySearchLabel")

if Page <= 0 {
Page = 1
}
if PageSize <= 0 || PageSize > 200 {
PageSize = setting.UI.IssuePagingNum
}
if Key != "" && !OnlyReturnNum {
go models.SaveSearchKeywordToDb(Key)
}
if TableName == "repository" {
if OnlySearchLabel {
searchRepoByLabel(ctx, Key, Page, PageSize)
} else {
searchRepo(ctx, "repository-es-index", Key, Page, PageSize, OnlyReturnNum)
}
return
} else if TableName == "issue" {
searchIssueOrPr(ctx, "issue-es-index", Key, Page, PageSize, OnlyReturnNum, "f")
return
} else if TableName == "user" {
searchUserOrOrg(ctx, "user-es-index", Key, Page, PageSize, true, OnlyReturnNum)
return
} else if TableName == "org" {
searchUserOrOrg(ctx, "user-es-index", Key, Page, PageSize, false, OnlyReturnNum)
return
} else if TableName == "dataset" {
searchDataSet(ctx, "dataset-es-index", Key, Page, PageSize, OnlyReturnNum)
return
} else if TableName == "pr" {
searchIssueOrPr(ctx, "issue-es-index", Key, Page, PageSize, OnlyReturnNum, "t")
//searchPR(ctx, "issue-es-index", Key, Page, PageSize, OnlyReturnNum)
return
}
}

func searchRepoByLabel(ctx *context.Context, Key string, Page int, PageSize int) {
/*
项目, ES名称: repository-es-index
搜索:
name character varying(255) , 项目名称
description text, 项目描述
topics json, 标签
排序:
updated_unix
num_watches,
num_stars,
num_forks,
*/
SortBy := ctx.Query("SortBy")
PrivateTotal := ctx.QueryInt("PrivateTotal")
WebTotal := ctx.QueryInt("WebTotal")
ascending := ctx.QueryBool("Ascending")
language := ctx.Query("language")
if language == "" {
language = "zh-CN"
}
from := (Page - 1) * PageSize
resultObj := &SearchRes{}
log.Info("WebTotal=" + fmt.Sprint(WebTotal))
log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
resultObj.Result = make([]map[string]interface{}, 0)
if from == 0 {
WebTotal = 0
}
if ctx.User != nil && (from < PrivateTotal || from == 0) {
orderBy := models.SearchOrderByRecentUpdated
switch SortBy {
case "updated_unix.keyword":
orderBy = models.SearchOrderByRecentUpdated
case "num_stars":
orderBy = models.SearchOrderByStarsReverse
case "num_forks":
orderBy = models.SearchOrderByForksReverse
case "num_watches":
orderBy = models.SearchOrderByWatches
}
log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
repos, count, err := models.SearchRepository(&models.SearchRepoOptions{
ListOptions: models.ListOptions{
Page: Page,
PageSize: PageSize,
},
Actor: ctx.User,
OrderBy: orderBy,
Private: true,
OnlyPrivate: true,
TopicOnly: true,
TopicName: Key,
IncludeDescription: setting.UI.SearchRepoDescription,
})
if err != nil {
ctx.JSON(200, "")
return
}
resultObj.PrivateTotal = count
if repos.Len() > 0 {
log.Info("Query private repo number is:" + fmt.Sprint(repos.Len()))
makePrivateRepo(repos, resultObj, Key, language)
} else {
log.Info("not found private repo,keyword=" + Key)
}
if repos.Len() >= PageSize {
if WebTotal > 0 {
resultObj.Total = int64(WebTotal)
ctx.JSON(200, resultObj)
return
}
}
} else {
if ctx.User == nil {
resultObj.PrivateTotal = 0
} else {
resultObj.PrivateTotal = int64(PrivateTotal)
}
}

from = from - PrivateTotal
if from < 0 {
from = 0
}
Size := PageSize - len(resultObj.Result)

log.Info("query searchRepoByLabel start")
if Key != "" {
boolQ := elastic.NewBoolQuery()
topicsQuery := elastic.NewMatchQuery("topics", Key)
boolQ.Should(topicsQuery)

res, err := client.Search("repository-es-index").Query(boolQ).SortBy(getSort(SortBy, ascending)).From(from).Size(Size).Highlight(queryHighlight("topics")).Do(ctx.Req.Context())
if err == nil {
searchJson, _ := json.Marshal(res)
log.Info("searchJson=" + string(searchJson))
esresult := makeRepoResult(res, "", false, language)
resultObj.Total = resultObj.PrivateTotal + esresult.Total
resultObj.Result = append(resultObj.Result, esresult.Result...)
ctx.JSON(200, resultObj)
} else {
log.Info("query es error," + err.Error())
ctx.JSON(200, "")
}
} else {
ctx.JSON(200, "")
}
}

func getSort(SortBy string, ascending bool) elastic.Sorter {
var sort elastic.Sorter
sort = elastic.NewScoreSort()
if SortBy != "" {
if SortBy == "default" {
return sort
}
return elastic.NewFieldSort(SortBy).Order(ascending)
}
return sort
}

func searchRepo(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool) {
/*
项目, ES名称: repository-es-index
搜索:
name character varying(255) , 项目名称
description text, 项目描述
topics json, 标签
排序:
updated_unix
num_watches,
num_stars,
num_forks,
*/

SortBy := ctx.Query("SortBy")
PrivateTotal := ctx.QueryInt("PrivateTotal")
WebTotal := ctx.QueryInt("WebTotal")
ascending := ctx.QueryBool("Ascending")
from := (Page - 1) * PageSize
resultObj := &SearchRes{}
log.Info("WebTotal=" + fmt.Sprint(WebTotal))
log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
resultObj.Result = make([]map[string]interface{}, 0)
if from == 0 {
WebTotal = 0
}
language := ctx.Query("language")
if language == "" {
language = "zh-CN"
}
if ctx.User != nil && (from < PrivateTotal || from == 0) {
orderBy := models.SearchOrderByRecentUpdated
switch SortBy {
case "updated_unix.keyword":
orderBy = models.SearchOrderByRecentUpdated
case "num_stars":
orderBy = models.SearchOrderByStarsReverse
case "num_forks":
orderBy = models.SearchOrderByForksReverse
case "num_watches":
orderBy = models.SearchOrderByWatches
}
log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
repos, count, err := models.SearchRepository(&models.SearchRepoOptions{
ListOptions: models.ListOptions{
Page: Page,
PageSize: PageSize,
},
Actor: ctx.User,
OrderBy: orderBy,
Private: true,
OnlyPrivate: true,
Keyword: Key,
IncludeDescription: setting.UI.SearchRepoDescription,
OnlySearchPrivate: true,
})
if err != nil {
ctx.JSON(200, "")
return
}
resultObj.PrivateTotal = count
if repos.Len() > 0 {
log.Info("Query private repo number is:" + fmt.Sprint(repos.Len()))
makePrivateRepo(repos, resultObj, Key, language)
} else {
log.Info("not found private repo,keyword=" + Key)
}
if repos.Len() >= PageSize {
if WebTotal > 0 {
resultObj.Total = int64(WebTotal)
ctx.JSON(200, resultObj)
return
}
}
} else {
if ctx.User == nil {
resultObj.PrivateTotal = 0
} else {
resultObj.PrivateTotal = int64(PrivateTotal)
}
}

from = from - PrivateTotal
if from < 0 {
from = 0
}
Size := PageSize - len(resultObj.Result)

log.Info("query searchRepo start")
if Key != "" {
boolQ := elastic.NewBoolQuery()
nameQuery := elastic.NewMatchQuery("alias", Key).Boost(1024).QueryName("f_first")
descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
topicsQuery := elastic.NewMatchQuery("topics", Key).Boost(1).QueryName("f_third")
boolQ.Should(nameQuery, descriptionQuery, topicsQuery)

res, err := client.Search(TableName).Query(boolQ).SortBy(getSort(SortBy, ascending)).From(from).Size(Size).Highlight(queryHighlight("alias", "description", "topics")).Do(ctx.Req.Context())
if err == nil {
searchJson, _ := json.Marshal(res)
log.Info("searchJson=" + string(searchJson))
esresult := makeRepoResult(res, Key, OnlyReturnNum, language)
resultObj.Total = resultObj.PrivateTotal + esresult.Total
isNeedSort := false
if len(resultObj.Result) > 0 {
isNeedSort = true
}
resultObj.Result = append(resultObj.Result, esresult.Result...)
if isNeedSort {
sortRepo(resultObj.Result, SortBy, ascending)
}
ctx.JSON(200, resultObj)
} else {
log.Info("query es error," + err.Error())
ctx.JSON(200, "")
}
} else {
log.Info("query all content.")
//搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
res, err := client.Search(TableName).SortBy(getSort(SortBy, ascending)).From(from).Size(Size).Do(ctx.Req.Context())
if err == nil {
searchJson, _ := json.Marshal(res)
log.Info("searchJson=" + string(searchJson))
esresult := makeRepoResult(res, "", OnlyReturnNum, language)
resultObj.Total = resultObj.PrivateTotal + esresult.Total
resultObj.Result = append(resultObj.Result, esresult.Result...)
ctx.JSON(200, resultObj)
} else {
log.Info("query es error," + err.Error())
ctx.JSON(200, "")
}
}
}

func sortRepo(Result []map[string]interface{}, SortBy string, ascending bool) {
orderBy := ""
switch SortBy {
case "updated_unix.keyword":
orderBy = "updated_unix"
case "num_stars":
orderBy = "num_stars"
case "num_forks":
orderBy = "num_forks"
case "num_watches":
orderBy = "num_watches"
}
sort.Slice(Result, func(i, j int) bool {
return getInt(Result[i][orderBy], orderBy) > getInt(Result[j][orderBy], orderBy)
})
}

func getInt(tmp interface{}, orderBy string) int64 {
timeInt, err := strconv.ParseInt(fmt.Sprint(tmp), 10, 64)
if err == nil {
return timeInt
} else {
log.Info("convert " + orderBy + " error type=" + fmt.Sprint(tmp))
}
return -1
}

func makePrivateRepo(repos models.RepositoryList, res *SearchRes, keyword string, language string) {

for _, repo := range repos {
record := make(map[string]interface{})
record["id"] = repo.ID
record["name"] = makeHighLight(keyword, repo.Name)
record["real_name"] = repo.Name
record["owner_name"] = repo.OwnerName
record["description"] = truncLongText(makeHighLight(keyword, repo.Description), true)

hightTopics := make([]string, 0)
if len(repo.Topics) > 0 {
for _, t := range repo.Topics {
hightTopics = append(hightTopics, makeHighLight(keyword, t))
}
}
record["hightTopics"] = hightTopics

record["num_watches"] = repo.NumWatches
record["num_stars"] = repo.NumStars
record["num_forks"] = repo.NumForks
record["alias"] = truncLongText(makeHighLight(keyword, repo.Alias), true)
record["lower_alias"] = repo.LowerAlias
record["topics"] = repo.Topics
record["avatar"] = repo.RelAvatarLink()
if len(repo.RelAvatarLink()) == 0 {
record["avatar"] = setting.RepositoryAvatarFallbackImage
}
record["updated_unix"] = repo.UpdatedUnix
record["updated_html"] = timeutil.TimeSinceUnix(repo.UpdatedUnix, language)
lang, err := repo.GetTopLanguageStats(1)
if err == nil && len(lang) > 0 {
record["lang"] = lang[0].Language
} else {
record["lang"] = ""
}
record["is_private"] = true
res.Result = append(res.Result, record)
}
}

func makeHighLight(keyword string, dest string) string {

dest = replaceIngoreUpperOrLower(dest, strings.ToLower(dest), strings.ToLower(keyword))

return dest
}

func replaceIngoreUpperOrLower(dest string, destLower string, keywordLower string) string {
re := ""
last := 0
lenDestLower := len(destLower)
lenkeywordLower := len(keywordLower)
for i := 0; i < lenDestLower; i++ {
if destLower[i] == keywordLower[0] {
isFind := true
for j := 1; j < lenkeywordLower; j++ {
if (i+j) < lenDestLower && keywordLower[j] != destLower[i+j] {
isFind = false
break
}
}
if isFind && (i+lenkeywordLower) <= lenDestLower {
re += dest[last:i] + "\u003cfont color='red'\u003e" + dest[i:(i+lenkeywordLower)] + "\u003c/font\u003e"
i = i + lenkeywordLower
last = i
}
}
}
if last < lenDestLower {
re += dest[last:lenDestLower]
}
return re
}

func makeRepoResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, language string) *SearchRes {
total := sRes.Hits.TotalHits.Value
result := make([]map[string]interface{}, 0)
if !OnlyReturnNum {
for i, hit := range sRes.Hits.Hits {
log.Info("this is repo query " + fmt.Sprint(i) + " result.")
recordSource := make(map[string]interface{})
source, err := hit.Source.MarshalJSON()

if err == nil {
err = json.Unmarshal(source, &recordSource)
if err == nil {
record := make(map[string]interface{})
record["id"] = hit.Id
record["alias"] = getLabelValue("alias", recordSource, hit.Highlight)
record["real_name"] = recordSource["name"]
record["owner_name"] = recordSource["owner_name"]
if recordSource["description"] != nil {
desc := getLabelValue("description", recordSource, hit.Highlight)
record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
} else {
record["description"] = ""
}

record["hightTopics"] = jsonStrToArray(getLabelValue("topics", recordSource, hit.Highlight))
record["num_watches"] = recordSource["num_watches"]
record["num_stars"] = recordSource["num_stars"]
record["num_forks"] = recordSource["num_forks"]
record["lower_alias"] = recordSource["lower_alias"]
if recordSource["topics"] != nil {
topicsStr := recordSource["topics"].(string)
log.Info("topicsStr=" + topicsStr)
if topicsStr != "null" {
record["topics"] = jsonStrToArray(topicsStr)
}
}
if recordSource["avatar"] != nil {
avatarstr := recordSource["avatar"].(string)
if len(avatarstr) == 0 {
record["avatar"] = setting.RepositoryAvatarFallbackImage
} else {
record["avatar"] = setting.AppSubURL + "/repo-avatars/" + avatarstr
}
}
record["updated_unix"] = recordSource["updated_unix"]
setUpdateHtml(record, recordSource["updated_unix"].(string), language)

record["lang"] = recordSource["lang"]
record["is_private"] = false
result = append(result, record)
} else {
log.Info("deal repo source error," + err.Error())
}
} else {
log.Info("deal repo source error," + err.Error())
}
}
}
returnObj := &SearchRes{
Total: total,
Result: result,
}

return returnObj
}

func setUpdateHtml(record map[string]interface{}, updated_unix string, language string) {
timeInt, err := strconv.ParseInt(updated_unix, 10, 64)
if err == nil {
record["updated_html"] = timeutil.TimeSinceUnix(timeutil.TimeStamp(timeInt), language)
}
}

func jsonStrToArray(str string) []string {
b := []byte(str)
strs := make([]string, 0)
err := json.Unmarshal(b, &strs)
if err != nil {
log.Info("convert str arrar error, str=" + str)
}
return strs
}

func dealLongText(text string, Key string, MatchedQueries []string) string {
var isNeedToDealText bool
isNeedToDealText = false
if len(MatchedQueries) > 0 && Key != "" {
if MatchedQueries[0] == "f_second" || MatchedQueries[0] == "f_third" {
isNeedToDealText = true
}
}
return truncLongText(text, isNeedToDealText)
}

func truncLongText(text string, isNeedToDealText bool) string {
startStr := "color="
textRune := []rune(text)
stringlen := len(textRune)
if isNeedToDealText && stringlen > 200 {
index := findFont(textRune, []rune(startStr))
if index > 0 {
start := index - 50
if start < 0 {
start = 0
}
end := index + 150
if end >= stringlen {
end = stringlen
}
return trimFontHtml(textRune[start:end]) + "..."
} else {
return trimFontHtml(textRune[0:200]) + "..."
}
} else {
if stringlen > 200 {
return trimFontHtml(textRune[0:200]) + "..."
} else {
return text
}
}
}

func trimFontHtml(text []rune) string {
startRune := rune('<')
endRune := rune('>')
count := 0
for i := 0; i < len(text); i++ {
if text[i] == startRune { //start <
re := false
j := i + 1
for ; j < len(text); j++ {
if text[j] == endRune {
re = true
break
}
}
if re { //found >
i = j
count++
} else {
if count%2 == 1 {
return string(text[0:i]) + "</font>"
} else {
return string(text[0:i])
}

}
}
}
return string(text)
}

func trimHrefHtml(result string) string {
result = strings.Replace(result, "</a>", "", -1)
result = strings.Replace(result, "\n", "", -1)
var index int
for {
index = findSubstr(result, 0, "<a")
if index != -1 {
sIndex := findSubstr(result, index+2, ">")
if sIndex != -1 {
result = result[0:index] + result[sIndex+1:]
} else {
result = result[0:index] + result[index+2:]
}
} else {
break
}
}
return result
}

func findFont(text []rune, childText []rune) int {
for i := 0; i < len(text); i++ {
if text[i] == childText[0] {
re := true
for j, k := range childText {
if k != text[i+j] {
re = false
break
}
}
if re {
return i
}
}
}
return -1
}

func findSubstr(text string, startindex int, childText string) int {
for i := startindex; i < len(text); i++ {
if text[i] == childText[0] {
re := true
for k := range childText {
if childText[k] != text[i+k] {
re = false
break
}
}
if re {
return i
}
}
}
return -1
}

func searchUserOrOrg(ctx *context.Context, TableName string, Key string, Page int, PageSize int, IsQueryUser bool, OnlyReturnNum bool) {
/*
用户或者组织 ES名称: user-es-index
搜索:
name , 名称
full_name 全名
description 描述或者简介
排序:
created_unix
名称字母序
*/
SortBy := ctx.Query("SortBy")
ascending := ctx.QueryBool("Ascending")
boolQ := elastic.NewBoolQuery()

typeValue := 1
if IsQueryUser {
typeValue = 0
}
UserOrOrgQuery := elastic.NewTermQuery("type", typeValue)
if Key != "" {
boolKeyQ := elastic.NewBoolQuery()
log.Info("user or org Key=" + Key)
nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
full_nameQuery := elastic.NewMatchQuery("full_name", Key).Boost(1.5).QueryName("f_second")
descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1).QueryName("f_third")
boolKeyQ.Should(nameQuery, full_nameQuery, descriptionQuery)
boolQ.Must(UserOrOrgQuery, boolKeyQ)
} else {
boolQ.Must(UserOrOrgQuery)
}

res, err := client.Search(TableName).Query(boolQ).SortBy(getSort(SortBy, ascending)).From((Page - 1) * PageSize).Size(PageSize).Highlight(queryHighlight("name", "full_name", "description")).Do(ctx.Req.Context())
if err == nil {
searchJson, _ := json.Marshal(res)
log.Info("searchJson=" + string(searchJson))
result := makeUserOrOrgResult(res, Key, ctx, OnlyReturnNum)
ctx.JSON(200, result)
} else {
log.Info("query es error," + err.Error())
ctx.JSON(200, "")
}
}

func getLabelValue(key string, recordSource map[string]interface{}, searchHighliht elastic.SearchHitHighlight) string {
if value, ok := searchHighliht[key]; !ok {
if recordSource[key] != nil {
return recordSource[key].(string)
} else {
return ""
}
} else {
return value[0]
}
}

func makeUserOrOrgResult(sRes *elastic.SearchResult, Key string, ctx *context.Context, OnlyReturnNum bool) *SearchRes {
total := sRes.Hits.TotalHits.Value
result := make([]map[string]interface{}, 0)
if !OnlyReturnNum {
for i, hit := range sRes.Hits.Hits {
log.Info("this is user query " + fmt.Sprint(i) + " result.")
recordSource := make(map[string]interface{})
source, err := hit.Source.MarshalJSON()

if err == nil {
err = json.Unmarshal(source, &recordSource)
if err == nil {
record := make(map[string]interface{})
record["id"] = hit.Id
record["name"] = getLabelValue("name", recordSource, hit.Highlight)
record["real_name"] = recordSource["name"]
record["full_name"] = getLabelValue("full_name", recordSource, hit.Highlight)
if recordSource["description"] != nil {
desc := getLabelValue("description", recordSource, hit.Highlight)
record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
} else {
record["description"] = ""
}
if ctx.User != nil {
record["email"] = recordSource["email"]
} else {
record["email"] = ""
}

record["location"] = recordSource["location"]
record["website"] = recordSource["website"]
record["num_repos"] = recordSource["num_repos"]
record["num_teams"] = recordSource["num_teams"]
record["num_members"] = recordSource["num_members"]

record["avatar"] = strings.TrimRight(setting.AppSubURL, "/") + "/user/avatar/" + recordSource["name"].(string) + "/" + strconv.Itoa(-1)
record["updated_unix"] = recordSource["updated_unix"]
record["created_unix"] = recordSource["created_unix"]
record["add_time"] = getAddTime(recordSource["created_unix"].(string))
result = append(result, record)
} else {
log.Info("deal user source error," + err.Error())
}
} else {
log.Info("deal user source error," + err.Error())
}
}
}
returnObj := &SearchRes{
Total: total,
Result: result,
}
return returnObj
}

func getAddTime(time string) string {
timeInt, err := strconv.ParseInt(time, 10, 64)
if err == nil {
t := timeutil.TimeStamp(timeInt)
return t.FormatShort()
}
return ""
}

func searchDataSet(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool) {
/*
数据集,ES名称:dataset-es-index
搜索:
title , 名称
description 描述
category 标签
file_name 数据集文件名称
排序:
download_times

*/
log.Info("query searchdataset start")
SortBy := ctx.Query("SortBy")
ascending := ctx.QueryBool("Ascending")
PrivateTotal := ctx.QueryInt("PrivateTotal")
WebTotal := ctx.QueryInt("WebTotal")
language := ctx.Query("language")
if language == "" {
language = "zh-CN"
}
from := (Page - 1) * PageSize
if from == 0 {
WebTotal = 0
}
resultObj := &SearchRes{}
log.Info("WebTotal=" + fmt.Sprint(WebTotal))
log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
resultObj.Result = make([]map[string]interface{}, 0)

if ctx.User != nil && (from < PrivateTotal || from == 0) {

log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
datasets, count, err := models.SearchDatasetBySQL(Page, PageSize, Key, ctx.User.ID)
if err != nil {
ctx.JSON(200, "")
return
}
resultObj.PrivateTotal = count
datasetSize := len(datasets)
if datasetSize > 0 {
log.Info("Query private dataset number is:" + fmt.Sprint(datasetSize) + " count=" + fmt.Sprint(count))
makePrivateDataSet(datasets, resultObj, Key, language)
} else {
log.Info("not found private dataset, keyword=" + Key)
}
if datasetSize >= PageSize {
if WebTotal > 0 { //next page, not first query.
resultObj.Total = int64(WebTotal)
ctx.JSON(200, resultObj)
return
}
}
} else {
resultObj.PrivateTotal = int64(PrivateTotal)
}

from = from - PrivateTotal
if from < 0 {
from = 0
}
Size := PageSize - len(resultObj.Result)

boolQ := elastic.NewBoolQuery()
if Key != "" {
nameQuery := elastic.NewMatchQuery("title", Key).Boost(2).QueryName("f_first")
descQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
fileNameQuery := elastic.NewMatchQuery("file_name", Key).Boost(1).QueryName("f_third")
categoryQuery := elastic.NewMatchQuery("category", Key).Boost(1).QueryName("f_fourth")
boolQ.Should(nameQuery, descQuery, categoryQuery, fileNameQuery)
res, err := client.Search(TableName).Query(boolQ).SortBy(getSort(SortBy, ascending)).From(from).Size(Size).Highlight(queryHighlight("title", "description", "file_name", "category")).Do(ctx.Req.Context())
if err == nil {
searchJson, _ := json.Marshal(res)
log.Info("searchJson=" + string(searchJson))
esresult := makeDatasetResult(res, Key, OnlyReturnNum, language)
resultObj.Total = resultObj.PrivateTotal + esresult.Total
log.Info("query dataset es count=" + fmt.Sprint(esresult.Total) + " total=" + fmt.Sprint(resultObj.Total))
resultObj.Result = append(resultObj.Result, esresult.Result...)
ctx.JSON(200, resultObj)
} else {
log.Info("query es error," + err.Error())
}
} else {
log.Info("query all datasets.")
//搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
res, err := client.Search(TableName).SortBy(getSort(SortBy, ascending)).From(from).Size(Size).Do(ctx.Req.Context())
if err == nil {
searchJson, _ := json.Marshal(res)
log.Info("searchJson=" + string(searchJson))
esresult := makeDatasetResult(res, "", OnlyReturnNum, language)
resultObj.Total = resultObj.PrivateTotal + esresult.Total
log.Info("query dataset es count=" + fmt.Sprint(esresult.Total) + " total=" + fmt.Sprint(resultObj.Total))
resultObj.Result = append(resultObj.Result, esresult.Result...)
ctx.JSON(200, resultObj)
} else {
log.Info("query es error," + err.Error())
ctx.JSON(200, "")
}
}

}

func makePrivateDataSet(datasets []*models.Dataset, res *SearchRes, Key string, language string) {
for _, dataset := range datasets {
record := make(map[string]interface{})

record["id"] = dataset.ID
userId := dataset.UserID

user, errUser := models.GetUserByID(userId)
if errUser == nil {
record["owerName"] = user.GetDisplayName()
record["avatar"] = user.RelAvatarLink()
}

repo, errRepo := models.GetRepositoryByID(dataset.RepoID)
if errRepo == nil {
log.Info("repo_url=" + repo.FullName())
record["repoUrl"] = repo.FullName()
record["avatar"] = repo.RelAvatarLink()
} else {
log.Info("repo err=" + errRepo.Error())
}

record["title"] = makeHighLight(Key, dataset.Title)
record["description"] = truncLongText(makeHighLight(Key, dataset.Description), true)

record["category"] = dataset.Category
record["task"] = dataset.Task
record["download_times"] = dataset.DownloadTimes
record["created_unix"] = dataset.CreatedUnix
record["updated_unix"] = repo.UpdatedUnix
record["updated_html"] = timeutil.TimeSinceUnix(repo.UpdatedUnix, language)

res.Result = append(res.Result, record)
}
}

func makeDatasetResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, language string) *SearchRes {
total := sRes.Hits.TotalHits.Value
result := make([]map[string]interface{}, 0)
if !OnlyReturnNum {
for i, hit := range sRes.Hits.Hits {
log.Info("this is dataset query " + fmt.Sprint(i) + " result.")
recordSource := make(map[string]interface{})
source, err := hit.Source.MarshalJSON()

if err == nil {
err = json.Unmarshal(source, &recordSource)
if err == nil {
record := make(map[string]interface{})
record["id"] = hit.Id
userIdStr := recordSource["user_id"].(string)
userId, cerr := strconv.ParseInt(userIdStr, 10, 64)
if cerr == nil {
user, errUser := models.GetUserByID(userId)
if errUser == nil {
record["owerName"] = user.GetDisplayName()
record["avatar"] = user.RelAvatarLink()
}
}
setRepoInfo(recordSource, record)
record["title"] = getLabelValue("title", recordSource, hit.Highlight)
record["category"] = getLabelValue("category", recordSource, hit.Highlight)
if recordSource["description"] != nil {
desc := getLabelValue("description", recordSource, hit.Highlight)
record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
} else {
record["description"] = ""
}
record["file_name"] = getDatasetFileName(getLabelValue("file_name", recordSource, hit.Highlight))
record["task"] = recordSource["task"]
record["download_times"] = recordSource["download_times"]
record["created_unix"] = recordSource["created_unix"]
setUpdateHtml(record, recordSource["updated_unix"].(string), language)
result = append(result, record)
} else {
log.Info("deal dataset source error," + err.Error())
}
} else {
log.Info("deal dataset source error," + err.Error())
}
}
}
returnObj := &SearchRes{
Total: total,
Result: result,
}

return returnObj
}

func getDatasetFileName(fileName string) string {
slices := strings.Split(fileName, "-#,#-")
fileName = strings.Join(slices, ", ")
return fileName
}

func searchIssueOrPr(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool, issueOrPr string) {

/*
任务,合并请求 ES名称:issue-es-index
搜索:
name character varying(255) , 标题
content text, 内容
comment text, 评论
排序:
updated_unix
*/
SortBy := ctx.Query("SortBy")
ascending := ctx.QueryBool("Ascending")
PrivateTotal := ctx.QueryInt("PrivateTotal")
WebTotal := ctx.QueryInt("WebTotal")
language := ctx.Query("language")
if language == "" {
language = "zh-CN"
}
from := (Page - 1) * PageSize
if from == 0 {
WebTotal = 0
}
resultObj := &SearchRes{}
log.Info("WebTotal=" + fmt.Sprint(WebTotal))
log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
resultObj.Result = make([]map[string]interface{}, 0)
isPull := false
if issueOrPr == "t" {
isPull = true
}

if ctx.User != nil && (from < PrivateTotal || from == 0) {

log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
issues, count, err := models.SearchPrivateIssueOrPr(Page, PageSize, Key, isPull, ctx.User.ID)
if err != nil {
ctx.JSON(200, "")
return
}
resultObj.PrivateTotal = count
issuesSize := len(issues)
if issuesSize > 0 {
log.Info("Query private repo issue number is:" + fmt.Sprint(issuesSize) + " count=" + fmt.Sprint(count))
makePrivateIssueOrPr(issues, resultObj, Key, language)
} else {
log.Info("not found private repo issue,keyword=" + Key)
}
if issuesSize >= PageSize {
if WebTotal > 0 { //next page, not first query.
resultObj.Total = int64(WebTotal)
ctx.JSON(200, resultObj)
return
}
}
} else {
resultObj.PrivateTotal = int64(PrivateTotal)
}

from = from - PrivateTotal
if from < 0 {
from = 0
}
Size := PageSize - len(resultObj.Result)

boolQ := elastic.NewBoolQuery()
isIssueQuery := elastic.NewTermQuery("is_pull", issueOrPr)

if Key != "" {
boolKeyQ := elastic.NewBoolQuery()
log.Info("issue Key=" + Key)
nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
contentQuery := elastic.NewMatchQuery("content", Key).Boost(1.5).QueryName("f_second")
commentQuery := elastic.NewMatchQuery("comment", Key).Boost(1).QueryName("f_third")
boolKeyQ.Should(nameQuery, contentQuery, commentQuery)
boolQ.Must(isIssueQuery, boolKeyQ)
} else {
boolQ.Must(isIssueQuery)
}

res, err := client.Search(TableName).Query(boolQ).SortBy(getSort(SortBy, ascending)).From(from).Size(Size).Highlight(queryHighlight("name", "content", "comment")).Do(ctx.Req.Context())
if err == nil {
searchJson, _ := json.Marshal(res)
log.Info("searchJson=" + string(searchJson))
esresult := makeIssueResult(res, Key, OnlyReturnNum, language)

resultObj.Total = resultObj.PrivateTotal + esresult.Total
log.Info("query issue es count=" + fmt.Sprint(esresult.Total) + " total=" + fmt.Sprint(resultObj.Total))
resultObj.Result = append(resultObj.Result, esresult.Result...)
ctx.JSON(200, resultObj)
} else {
log.Info("query es error," + err.Error())
}
}

func queryHighlight(names ...string) *elastic.Highlight {
re := elastic.NewHighlight()
for i := 0; i < len(names); i++ {
field := &elastic.HighlighterField{
Name: names[i],
}
re.Fields(field)
}
re.PreTags("<font color='red'>")
re.PostTags("</font>")
return re
}

func setRepoInfo(recordSource map[string]interface{}, record map[string]interface{}) {
repoIdstr := recordSource["repo_id"].(string)
repoId, cerr := strconv.ParseInt(repoIdstr, 10, 64)
if cerr == nil {
repo, errRepo := models.GetRepositoryByID(repoId)
if errRepo == nil {
log.Info("repo_url=" + repo.FullName())
record["repoUrl"] = repo.FullName()
record["avatar"] = repo.RelAvatarLink()
} else {
log.Info("repo err=" + errRepo.Error())
}
} else {
log.Info("parse int err=" + cerr.Error())
}
}

func makePrivateIssueOrPr(issues []*models.Issue, res *SearchRes, Key string, language string) {
for _, issue := range issues {
record := make(map[string]interface{})
record["id"] = issue.ID
record["repo_id"] = issue.RepoID

repo, errRepo := models.GetRepositoryByID(issue.RepoID)
if errRepo == nil {
log.Info("repo_url=" + repo.FullName())
record["repoUrl"] = repo.FullName()
record["avatar"] = repo.RelAvatarLink()
} else {
log.Info("repo err=" + errRepo.Error())
}
record["name"] = makeHighLight(Key, issue.Title)
record["content"] = truncLongText(makeHighLight(Key, issue.Content), true)

if issue.IsPull {
pr, err1 := issue.GetPullRequest()
if err1 == nil && pr != nil {
record["pr_id"] = pr.ID
}
}
record["index"] = issue.Index
record["num_comments"] = issue.NumComments
record["is_closed"] = issue.IsClosed
record["updated_unix"] = issue.UpdatedUnix
record["updated_html"] = timeutil.TimeSinceUnix(repo.UpdatedUnix, language)
res.Result = append(res.Result, record)
}
}

func makeIssueResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, language string) *SearchRes {
total := sRes.Hits.TotalHits.Value
result := make([]map[string]interface{}, 0)
if !OnlyReturnNum {
for i, hit := range sRes.Hits.Hits {
log.Info("this is issue query " + fmt.Sprint(i) + " result.")
recordSource := make(map[string]interface{})
source, err := hit.Source.MarshalJSON()

if err == nil {
err = json.Unmarshal(source, &recordSource)
if err == nil {
record := make(map[string]interface{})
record["id"] = hit.Id
record["repo_id"] = recordSource["repo_id"]
log.Info("recordSource[\"repo_id\"]=" + fmt.Sprint(recordSource["repo_id"]))
setRepoInfo(recordSource, record)
record["name"] = getLabelValue("name", recordSource, hit.Highlight)
if recordSource["content"] != nil {
desc := getLabelValue("content", recordSource, hit.Highlight)
record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
if _, ok := hit.Highlight["content"]; !ok {
if _, ok_comment := hit.Highlight["comment"]; ok_comment {
desc := getLabelValue("comment", recordSource, hit.Highlight)
record["content"] = trimHrefHtml(dealLongText(desc, Key, hit.MatchedQueries))
}
}
} else {
if recordSource["comment"] != nil {
desc := getLabelValue("comment", recordSource, hit.Highlight)
record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
}
}
if recordSource["pr_id"] != nil {
record["pr_id"] = recordSource["pr_id"]
}
log.Info("index=" + recordSource["index"].(string))
record["index"] = recordSource["index"]
record["num_comments"] = recordSource["num_comments"]
record["is_closed"] = recordSource["is_closed"]
record["updated_unix"] = recordSource["updated_unix"]
setUpdateHtml(record, recordSource["updated_unix"].(string), language)
result = append(result, record)
} else {
log.Info("deal issue source error," + err.Error())
}
} else {
log.Info("deal issue source error," + err.Error())
}
}
}
returnObj := &SearchRes{
Total: total,
Result: result,
}

return returnObj
}

+ 4
- 4
templates/base/head_navbar.tmpl View File

@@ -95,9 +95,9 @@

{{if .IsSigned}}
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="hot">
@@ -199,9 +199,9 @@
<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a-->
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="hot">


+ 4
- 4
templates/base/head_navbar_fluid.tmpl View File

@@ -93,9 +93,9 @@

{{if .IsSigned}}
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
@@ -196,9 +196,9 @@

<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a-->
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">


+ 4
- 4
templates/base/head_navbar_pro.tmpl View File

@@ -96,9 +96,9 @@

{{if .IsSigned}}
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
@@ -199,9 +199,9 @@

<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a-->
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">


+ 95
- 0
templates/explore/search_new.tmpl View File

@@ -0,0 +1,95 @@
{{template "base/head" .}}

<div class="explore seach">
<div class="repos--seach">
<div class="ui container">
<div id="search_div" class="ui two column centered grid">
<div class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin-top:1.2rem;margin-bottom: 1.2rem;">
<div class="ui fluid action input">
<input name="q" id="keyword_input" value="{{.Keyword}}" placeholder="{{.i18n.Tr "home.search"}}..." autofocus="">
<input type="hidden" name="topic" value="">
<input type="hidden" name="tab" value="">
<input type="hidden" name="sort" value="hot">
<button class="ui green button" onclick="search()">{{.i18n.Tr "home.search"}}</button>
</div>
</div>
</div>
<div id="search_label_div" style="display:none">
</div>
</div>

<div class="ui container seachnav">
<div class="ui secondary pointing menu">
<a id="repo_item" class="item" href="javascript:searchItem(1,10);">
{{.i18n.Tr "home.search_repo"}}
<span class="ui circular mini label" id="repo_total"></span>
</a>
<a id="dataset_item" class="item" href="javascript:searchItem(5,50);">
{{.i18n.Tr "home.search_dataset"}}
<span class="ui circular mini label" id="dataset_total"></span>
</a>
<a id="issue_item" class="item" href="javascript:searchItem(2,20);">
{{.i18n.Tr "home.search_issue"}}
<span class="ui circular mini label" id="issue_total"></span>
</a>
<a id="pr_item" class="item" href="javascript:searchItem(6,60);">
{{.i18n.Tr "home.search_pr"}}
<span class="ui circular mini label" id="pr_total"></span>
</a>
<a id="user_item" class="item" href="javascript:searchItem(3,30);">
{{.i18n.Tr "home.search_user"}}
<span class="ui circular mini label" id="user_total"></span>
</a>
<a id="org_item" class="item" href="javascript:searchItem(4,40);">
{{.i18n.Tr "home.search_org"}} <span class="ui circular mini label" id="org_total"></span>
</a>
</div>
</div>
</div>

<div class="ui container">
<span id="find_id">{{.i18n.Tr "home.search_finded"}}</span><span id="find_title"></span>
<div class="ui right floated secondary filter menu">
<!-- Sort -->
<div class="ui right dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_sort"}}
<i class="dropdown icon"></i>
</span>
<div class="menu" id="sort_type">
</div>
</div>

</div>
<div class="ui divider" style="margin-top: 1.25rem;"></div>

<div class="ui very relaxed divided list" id="child_search_item">

</div><!--seach list end-->
<div class="center page buttons" style="margin: 0px auto 15px">
<div class="ui borderless mini pagination menu" id="page_menu">
</div>
</div>
<div id="tipmsg"></div>
</div>

</div>
<script src="/self/js/jquery.min.js" type="text/javascript"></script>
<script src="/home/search.js?v={{MD5 AppVer}}" type="text/javascript"></script>
<div class="am-mt-30"></div>


{{template "base/footer" .}}

+ 109
- 0
web_src/less/openi.less View File

@@ -628,6 +628,32 @@ display: block;
.a_margin{
margin: 0px !important;
}

/*pages*/
.ui.borderless.pagination {border:none}
.ui.pagination.menu .item {
min-width: 32px;
text-align: center;
height: 32px;
border-radius: .28571429rem;
margin: 0 5px;
background-color: #F2F2F2;
}
.ui.pagination.menu>.item:first-child, .ui.pagination.menu .item:last-child {
background-color: #FFF !important;
}
.ui.ui.menu .item.disabled{
background-color: #F2F2F2;
}
.ui.pagination.menu .active.item {
background-color: #3291F8;
color: #FFF;
}
.ui.pagination.menu .item>.input {
margin: 0em .5em;
width: 3em;
height: 32px;
}
@media only screen and (max-width: 767px) {
.following.bar #navbar .brand{
padding-top: 6px;
@@ -783,4 +809,87 @@ display: block;
border: none !important;
color: #0366d6 !important;
box-shadow: -15px 0px 10px #fff;
}

/**seach**/
/**搜索导航条适配窄屏**/
.seachnav{
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none; /* firefox */
-ms-overflow-style: none; /* IE 10+ */
}
.seachnav::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
.ui.green.button, .ui.green.buttons .button{
background-color: #5BB973;
}
.seach .repos--seach{
padding-bottom: 0;
border-bottom: none;
}
.seach .ui.secondary.pointing.menu{
border-bottom: none;
}
.seach .ui.secondary.pointing.menu .item > i{
margin-right: 5px;
}
.seach .ui.secondary.pointing.menu .active.item{
border-bottom-width: 2px;
margin: 0 0 -1px;
}
.seach .ui.menu .active.item>.label {
background: #1684FC;
color: #FFF;
}
.seach .ui.menu .item>.label:not(.active.item>.label) {
background: #e8e8e8;
color: rgba(0,0,0,.6);
}
.highlight{
color: red;
}
.ui.list .list>.item>img.image+.content, .ui.list>.item>img.image+.content {
width: calc(100% - 4.0em);
margin-left: 0;
}
.seach .ui.list .list>.item .header, .seach .ui.list>.item .header{
margin-bottom: 0.5em;
font-size: 1.4rem !important;
font-weight: normal;
}
.seach .time, .seach .time a{
font-size: 12px;
color: grey;
}
.seach .list .item.members .ui.avatar.image {
width: 3.2em;
height: 3.2em;
}
.ui.list .list>.item.members>img.image+.content, .ui.list>.item.members>img.image+.content {
width: calc(100% - 4.0em);
margin-left: 0;
}

.searchlabel{
color: rgba(16, 16, 16, 100);
font-size: 24px;
text-align: left;
font-family: SourceHanSansSC-medium;
}

.hiddenSearch{
margin: auto;
display: none;
}

#tipmsg {
display: none;
z-index: 9999;
width:150;
height: 80;
}

Loading…
Cancel
Save