diff --git a/custom/public/css/git.openi.css b/custom/public/css/git.openi.css index c6ada7b28..a4920eec0 100644 --- a/custom/public/css/git.openi.css +++ b/custom/public/css/git.openi.css @@ -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{ diff --git a/models/dbsql/dataset_foreigntable_for_es.sql b/models/dbsql/dataset_foreigntable_for_es.sql new file mode 100644 index 000000000..815b89d02 --- /dev/null +++ b/models/dbsql/dataset_foreigntable_for_es.sql @@ -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; diff --git a/models/dbsql/issue_foreigntable_for_es.sql b/models/dbsql/issue_foreigntable_for_es.sql new file mode 100644 index 000000000..bb5c1634e --- /dev/null +++ b/models/dbsql/issue_foreigntable_for_es.sql @@ -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; \ No newline at end of file diff --git a/models/dbsql/repo_foreigntable_for_es.sql b/models/dbsql/repo_foreigntable_for_es.sql new file mode 100644 index 000000000..f51155ccf --- /dev/null +++ b/models/dbsql/repo_foreigntable_for_es.sql @@ -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; \ No newline at end of file diff --git a/models/dbsql/user_foreigntable_for_es.sql b/models/dbsql/user_foreigntable_for_es.sql new file mode 100644 index 000000000..c3d21b92a --- /dev/null +++ b/models/dbsql/user_foreigntable_for_es.sql @@ -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; \ No newline at end of file diff --git a/models/models.go b/models/models.go index 36527f78d..362d46618 100755 --- a/models/models.go +++ b/models/models.go @@ -138,6 +138,7 @@ func init() { new(OfficialTag), new(OfficialTagRepos), new(WechatBindLog), + new(SearchRecord), ) tablesStatistic = append(tablesStatistic, diff --git a/models/repo.go b/models/repo.go index 42e350fbe..b5d4921e4 100755 --- a/models/repo.go +++ b/models/repo.go @@ -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" diff --git a/models/repo_list.go b/models/repo_list.go index 6fb9380de..5bf0ecf03 100755 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -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)}) diff --git a/models/search_record.go b/models/search_record.go new file mode 100644 index 000000000..d9d85a591 --- /dev/null +++ b/models/search_record.go @@ -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 + } + +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 3fc66c426..26f068193 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -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") diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c715ba8ce..09bb5015f 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -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 diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index b47a6bafa..d26065363 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -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=精选项目 diff --git a/public/home/search.js b/public/home/search.js new file mode 100644 index 000000000..70b5d4ef9 --- /dev/null +++ b/public/home/search.js @@ -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="

#" + keyword + "

"; + + 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 +=""+ getLabel(isZh,"search_matched") + ""; + sortHtml +=""+ getLabel(isZh,"search_matched_download") + ""; + 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 += "
"; + html += "
"; + html += "
" ; + if(!isEmpty(recordMap["category"])){ + html += " " + getCategoryDesc(isZh,recordMap["category"]) + ""; + } + if(!isEmpty(recordMap["task"])){ + html += " " + getTaskDesc(isZh,recordMap["task"]) + ""; + } + html += " " +recordMap["download_times"] + " "; + html +="
"; + html += "
"; + html += " " + recordMap["title"] + ""; + html +=" "; + html +="
"; + html += "
"; + html += "

" + recordMap["description"] + "

"; + if(!isEmpty(recordMap["file_name"])){ + html += "

" + recordMap["file_name"] + "

"; + } + html +="

"; + html +=" "+ getLabel(isZh,"search_lasted_update") + " " + recordMap["updated_html"]; + html +="

"; + html +="
"; + html +="
"; + html +="
"; + } + 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 +=""+ getLabel(isZh,"search_matched") + ""; + sortHtml +=""+ getLabel(isZh,"search_letter_asc") + ""; + sortHtml +=""+ getLabel(isZh,"search_letter_desc") + ""; + sortHtml +=""+ getLabel(isZh,"search_lasted_create") + ""; + sortHtml +=""+ getLabel(isZh,"search_early_create") + ""; + 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 += "
"; + html += ""; + html += "
"; + html += " "; + html += "
"; + html += "

" + recordMap["description"] + "

"; + html +="

"; + if(!isEmpty(recordMap["location"]) && recordMap["location"] != "null"){ + html +=" " + recordMap["location"]; + } + html +=" "; + if(!isEmpty(recordMap["website"]) && recordMap["website"] != "null"){ + html +=" " + "" + recordMap["website"] + ""; + } + html +=" "+ getLabel(isZh,"search_add_by") + " "; + html += recordMap["add_time"] + html +="

"; + html +="
"; + html +="
"; + html +="
"; + } + 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 +=""+ getLabel(isZh,"search_matched") + ""; + sortHtml +=""+ getLabel(isZh,"search_letter_asc") + ""; + sortHtml +=""+ getLabel(isZh,"search_letter_desc") + ""; + sortHtml +=""+ getLabel(isZh,"search_lasted_create") + ""; + sortHtml +=""+ getLabel(isZh,"search_early_create") + ""; + + 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 += "
"; + html += ""; + html += "
"; + html += " "; + html += "
"; + html += "

" + recordMap["description"] + "

"; + html +="

"; + if(!isEmpty(recordMap["email"]) && recordMap["email"] != "null"){ + html +="  " + recordMap["email"] + ""; + } + html +=" "+ getLabel(isZh,"search_add_by") + " "; + html += recordMap["add_time"] + html +="

"; + html +="
"; + html +="
"; + html +="
"; + } + document.getElementById("child_search_item").innerHTML=html; + } +} + +function setIssueOrPrInnerHtml(data,path){ + var sortHtml = ""; + if(path =="issues"){ + sortHtml +=""+ getLabel(isZh,"search_matched") + ""; + sortHtml +=""+ getLabel(isZh,"search_lasted") + ""; + }else{ + sortHtml +=""+ getLabel(isZh,"search_matched") + ""; + sortHtml +=""+ getLabel(isZh,"search_lasted") + ""; + } + + 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 += "
"; + html += "
"; + html += "
"; + html += " " + recordMap["name"] + ""; + html +="
"; + html += "
"; + html += "

" + recordMap["content"] + "

"; + html +="

"; + html +=" "; + html +=" " + addBlank(recordMap["repoUrl"]) +" #" + recordMap["index"] + "    "; + html +="  "; + if(recordMap["is_closed"] != null && (!(recordMap["is_closed"]) || recordMap["is_closed"]=="f")){ + html += getLabel(isZh,"search_open"); + }else{ + html += getLabel(isZh,"search_closed"); + } + html +="      " + recordMap["num_comments"]; + + html +="     "+ getLabel(isZh,"search_lasted_update") + " "+ recordMap["updated_html"]; + + html +="

"; + html +="
"; + html +="
"; + html +="
"; + } + 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 +=""+ getLabel(isZh,"search_matched") + ""; + sortHtml +=""+ getLabel(isZh,"search_lasted") + ""; + sortHtml +=""+ getLabel(isZh,"search_watched") + ""; + sortHtml +=""+ getLabel(isZh,"search_star") + ""; + sortHtml +=""+ getLabel(isZh,"search_fork") + ""; + + 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 += "
"; + if(!isEmpty(recordMap['avatar'])){ + html += ""; + } + html += "
"; + html += "
"; + html += " " + recordMap["owner_name"] +" / " + recordMap["alias"] + ""; + if(recordMap["is_private"]){ + html +=" "; + } + html +="
"; + html += "
"; + html += "

" + recordMap["description"] + "

"; + html += "
"; + if(!isEmpty(recordMap["topics"]) && recordMap["topics"] !="null"){ + for(var j = 0; j < recordMap["topics"].length;j++){ + //function searchLabel(tableName,keyword,sortBy="",ascending=false) + html +="
"+ recordMap["hightTopics"][j] + "
"; + } + } + html +="
"; + html +="

"; + html +="  " + recordMap["num_watches"] + "   " + recordMap["num_stars"] + "   " + recordMap["num_forks"] +"  "; + html +="    "+ 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 +="  " + lang + ""; + } + html +="

"; + html +="
"; + html +="
"; + html +="
"; + } + + 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 ="

" + tip + "

" + $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 += "" + getLabel(isZh,"search_input_total") + " " + totalNum + " " + getLabel(isZh,"search_srtip") + "" + if(currentPage > 1){ + html += "" + getLabel(isZh,"search_home_page") + ""; + html += ""; + }else{ + html += "" + getLabel(isZh,"search_home_page") + ""; + html += ""; + } + + for(var i=startIndex; i <= endIndex; i++){ + var page_i = i; + if(page_i > totalPage){ + break; + } + if( page_i == currentPage){ + html += "" + page_i + ""; + }else{ + html += "" + page_i + ""; + } + } + + if(currentPage >=totalPage){ + html += ""; + html += "" + getLabel(isZh,"search_last_page") + ""; + }else{ + html += ""; + html += "" + getLabel(isZh,"search_last_page") + ""; + } + + html +="
" + getLabel(isZh,"search_go_to") + "
" + getLabel(isZh,"search_go_page") + "
"; + 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":"“{keyword}”相关{tablename}约为{total}个", + "search_empty":"请输入任意关键字开始搜索。" + } + + 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} \"{keyword}\" related {tablename}", + "search_empty":"Please enter any keyword to start the search." + } + 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(); + } + } + } + + diff --git a/public/self/dataset_preview.js b/public/self/dataset_preview.js index 3c9ded0aa..e6b79dd7d 100644 --- a/public/self/dataset_preview.js +++ b/public/self/dataset_preview.js @@ -620,10 +620,10 @@ function showfilelist(){ for (var i=0;i 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' diff --git a/routers/init.go b/routers/init.go index 8b93b64d8..eab513c78 100755 --- a/routers/init.go +++ b/routers/init.go @@ -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 diff --git a/routers/routes/routes.go b/routers/routes/routes.go index b15941dfc..4cffcd10b 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -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) { diff --git a/routers/search.go b/routers/search.go new file mode 100644 index 000000000..bc1bc5fac --- /dev/null +++ b/routers/search.go @@ -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]) + "" + } else { + return string(text[0:i]) + } + + } + } + } + return string(text) +} + +func trimHrefHtml(result string) string { + result = strings.Replace(result, "", "", -1) + result = strings.Replace(result, "\n", "", -1) + var index int + for { + index = findSubstr(result, 0, "") + 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("") + re.PostTags("") + 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 +} diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 0ce3f2fcf..18cf3ce0e 100755 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -95,9 +95,9 @@ {{if .IsSigned}}