Commit 0355e90a authored by Mark Chao's avatar Mark Chao

ES: Decouple other ActiveRecord models

See https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/14428
parent 61a2d19f
......@@ -100,11 +100,7 @@ def instrument_classes(instrumentation)
instrumentation.instrument_instance_methods(Gitlab::Elastic::SnippetSearchResults)
instrumentation.instrument_methods(Gitlab::Elastic::Helper)
instrumentation.instrument_instance_methods(Elastic::ApplicationSearch)
instrumentation.instrument_instance_methods(Elastic::IssuesSearch)
instrumentation.instrument_instance_methods(Elastic::MergeRequestsSearch)
instrumentation.instrument_instance_methods(Elastic::MilestonesSearch)
instrumentation.instrument_instance_methods(Elastic::NotesSearch)
instrumentation.instrument_instance_methods(Elastic::ApplicationVersionedSearch)
instrumentation.instrument_instance_methods(Elastic::ProjectsSearch)
instrumentation.instrument_instance_methods(Elastic::RepositoriesSearch)
instrumentation.instrument_instance_methods(Elastic::SnippetsSearch)
......
# frozen_string_literal: true
module Elastic
module ApplicationSearch
extend ActiveSupport::Concern
# Defer evaluation from class-definition time to index-creation time
class AsJSON
def initialize(&blk)
@blk = blk
end
def call
@blk.call
end
def as_json(*args, &blk)
call
end
end
included do
include Elasticsearch::Model
index_name [Rails.application.class.parent_name.downcase, Rails.env].join('-')
# ES6 requires a single type per index
document_type 'doc'
# A temp solution to keep only one copy of setting,
# will be removed in https://gitlab.com/gitlab-org/gitlab-ee/issues/12548
__elasticsearch__.instance_variable_set(:@settings, Elastic::Latest::Config.settings)
__elasticsearch__.instance_variable_set(:@mapping, Elastic::Latest::Config.mappings)
after_commit on: :create do
if Gitlab::CurrentSettings.elasticsearch_indexing? && self.searchable?
ElasticIndexerWorker.perform_async(:index, self.class.to_s, self.id, self.es_id)
end
end
after_commit on: :update do
if Gitlab::CurrentSettings.elasticsearch_indexing? && self.searchable?
ElasticIndexerWorker.perform_async(
:update,
self.class.to_s,
self.id,
self.es_id,
changed_fields: self.previous_changes.keys
)
end
end
after_commit on: :destroy do
if Gitlab::CurrentSettings.elasticsearch_indexing? && self.searchable?
ElasticIndexerWorker.perform_async(
:delete,
self.class.to_s,
self.id,
self.es_id,
es_parent: self.es_parent
)
end
end
# Should be overridden in the models where some records should be skipped
def searchable?
self.use_elasticsearch?
end
def use_elasticsearch?
self.project&.use_elasticsearch?
end
def generic_attributes
{
'join_field' => {
'name' => es_type,
'parent' => es_parent
},
'type' => es_type
}
end
def es_parent
"project_#{project_id}" unless is_a?(Project) || self&.project_id.nil?
end
def es_type
self.class.es_type
end
def es_id
"#{es_type}_#{id}"
end
# Some attributes are actually complicated methods. Bad data can cause
# them to raise exceptions. When this happens, we still want the remainder
# of the object to be saved, so silently swallow the errors
def safely_read_attribute_for_elasticsearch(attr_name)
send(attr_name) # rubocop:disable GitlabSecurity/PublicSend
rescue => err
logger.warn("Elasticsearch failed to read #{attr_name} for #{self.class} #{self.id}: #{err}")
nil
end
end
class_methods do
# Support STI models
def inherited(subclass)
super
# Avoid SystemStackError in Model.import
# See https://github.com/elastic/elasticsearch-rails/issues/144
subclass.include Elasticsearch::Model
# Use ES configuration from parent model
# TODO: Revisit after upgrading to elasticsearch-model 7.0.0
# See https://github.com/elastic/elasticsearch-rails/commit/b8455db186664e21927bfb271bab6390853e7ff3
subclass.__elasticsearch__.index_name = self.index_name
subclass.__elasticsearch__.document_type = self.document_type
subclass.__elasticsearch__.instance_variable_set(:@mapping, self.mapping.dup)
end
# Should be overridden for all nested models
def nested?
false
end
def es_type
name.underscore
end
def highlight_options(fields)
es_fields = fields.map { |field| field.split('^').first }.each_with_object({}) do |field, memo|
memo[field.to_sym] = {}
end
{ fields: es_fields }
end
def es_import(**options)
transform = lambda do |r|
{ index: { _id: r.es_id, data: r.__elasticsearch__.as_indexed_json } }.tap do |data|
data[:index][:routing] = r.es_parent if r.es_parent
end
end
options[:transform] = transform
self.import(options)
end
def basic_query_hash(fields, query)
query_hash = if query.present?
{
query: {
bool: {
must: [{
simple_query_string: {
fields: fields,
query: query,
default_operator: :and
}
}],
filter: [{
term: { type: self.es_type }
}]
}
}
}
else
{
query: {
bool: {
must: { match_all: {} }
}
},
track_scores: true
}
end
query_hash[:sort] = [
{ updated_at: { order: :desc } },
:_score
]
query_hash[:highlight] = highlight_options(fields)
query_hash
end
def iid_query_hash(iid)
{
query: {
bool: {
filter: [{ term: { iid: iid } }]
}
}
}
end
# Builds an elasticsearch query that will select child documents from a
# set of projects, taking user access rules into account.
def project_ids_filter(query_hash, options)
project_query = project_ids_query(
options[:current_user],
options[:project_ids],
options[:public_and_internal_projects],
options[:features]
)
query_hash[:query][:bool][:filter] ||= []
query_hash[:query][:bool][:filter] << {
has_parent: {
parent_type: "project",
query: {
bool: project_query
}
}
}
query_hash
end
# Builds an elasticsearch query that will select projects the user is
# granted access to.
#
# If a project feature(s) is specified, it indicates interest in child
# documents gated by that project feature - e.g., "issues". The feature's
# visibility level must be taken into account.
def project_ids_query(user, project_ids, public_and_internal_projects, features = nil)
# When reading cross project is not allowed, only allow searching a
# a single project, so the `:read_*` ability is only checked once.
unless Ability.allowed?(user, :read_cross_project)
project_ids = [] if project_ids.is_a?(Array) && project_ids.size > 1
end
# At least one condition must be present, so pick no projects for
# anonymous users.
# Pick private, internal and public projects the user is a member of.
# Pick all private projects for admins & auditors.
conditions = [pick_projects_by_membership(project_ids, features)]
if public_and_internal_projects
# Skip internal projects for anonymous and external users.
# Others are given access to all internal projects. Admins & auditors
# get access to internal projects where the feature is private.
conditions << pick_projects_by_visibility(Project::INTERNAL, user, features) if user && !user.external?
# All users, including anonymous, can access public projects.
# Admins & auditors get access to public projects where the feature is
# private.
conditions << pick_projects_by_visibility(Project::PUBLIC, user, features)
end
{ should: conditions }
end
private
# Most users come with a list of projects they are members of, which may
# be a mix of public, internal or private. Grant access to them all, as
# long as the project feature is not disabled.
#
# Admins & auditors are given access to all private projects. Access to
# internal or public projects where the project feature is private is not
# granted here.
def pick_projects_by_membership(project_ids, features = nil)
condition =
if project_ids == :any
{ term: { visibility_level: Project::PRIVATE } }
else
{ terms: { id: project_ids } }
end
limit_by_feature(condition, features, include_members_only: true)
end
# Grant access to projects of the specified visibility level to the user.
#
# If a project feature is specified, access is only granted if the feature
# is enabled or, for admins & auditors, private.
def pick_projects_by_visibility(visibility, user, features)
condition = { term: { visibility_level: visibility } }
limit_by_feature(condition, features, include_members_only: user&.full_private_access?)
end
# If a project feature(s) is specified, access is dependent on its visibility
# level being enabled (or private if `include_members_only: true`).
#
# This method is a no-op if no project feature is specified.
# It accepts an array of features or a single feature, when an array is provided
# it queries if any of the features is enabled.
#
# Always denies access to projects when the features are disabled - even to
# admins & auditors - as stale child documents may be present.
def limit_by_feature(condition, features, include_members_only:)
return condition unless features
features = Array(features)
features.map do |feature|
limit =
if include_members_only
{ terms: { "#{feature}_access_level" => [::ProjectFeature::ENABLED, ::ProjectFeature::PRIVATE] } }
else
{ term: { "#{feature}_access_level" => ::ProjectFeature::ENABLED } }
end
{ bool: { filter: [condition, limit] } }
end
end
end
end
end
# frozen_string_literal: true
module Elastic
module IssuesSearch
extend ActiveSupport::Concern
included do
include ApplicationSearch
def as_indexed_json(options = {})
data = {}
# We don't use as_json(only: ...) because it calls all virtual and serialized attributtes
# https://gitlab.com/gitlab-org/gitlab-ee/issues/349
[:id, :iid, :title, :description, :created_at, :updated_at, :state, :project_id, :author_id, :confidential].each do |attr|
data[attr.to_s] = safely_read_attribute_for_elasticsearch(attr)
end
data['assignee_id'] = safely_read_attribute_for_elasticsearch(:assignee_ids)
data.merge(generic_attributes)
end
def self.nested?
true
end
def self.elastic_search(query, options: {})
query_hash =
if query =~ /#(\d+)\z/
iid_query_hash(Regexp.last_match(1))
else
basic_query_hash(%w(title^2 description), query)
end
options[:features] = 'issues'
query_hash = project_ids_filter(query_hash, options)
query_hash = confidentiality_filter(query_hash, options[:current_user])
self.__elasticsearch__.search(query_hash)
end
def self.confidentiality_filter(query_hash, current_user)
return query_hash if current_user && current_user.full_private_access?
filter = if current_user
{
bool: {
should: [
{ term: { confidential: false } },
{
bool: {
must: [
{ term: { confidential: true } },
{
bool: {
should: [
{ term: { author_id: current_user.id } },
{ term: { assignee_id: current_user.id } },
{ terms: { project_id: current_user.authorized_projects(Gitlab::Access::REPORTER).pluck(:id) } }
]
}
}
]
}
}
]
}
}
else
{ term: { confidential: false } }
end
query_hash[:query][:bool][:filter] << filter
query_hash
end
end
end
end
......@@ -4,13 +4,7 @@ module Elastic
module ProjectsSearch
extend ActiveSupport::Concern
TRACKED_FEATURE_SETTINGS = %w(
issues_access_level
merge_requests_access_level
snippets_access_level
wiki_access_level
repository_access_level
).freeze
include ApplicationVersionedSearch
INDEXED_ASSOCIATIONS = [
:issues,
......@@ -21,97 +15,10 @@ module Elastic
].freeze
included do
include ApplicationSearch
def use_elasticsearch?
::Gitlab::CurrentSettings.elasticsearch_indexes_project?(self)
end
def as_indexed_json(options = {})
# We don't use as_json(only: ...) because it calls all virtual and serialized attributtes
# https://gitlab.com/gitlab-org/gitlab-ee/issues/349
data = {}
[
:id,
:name,
:path,
:description,
:namespace_id,
:created_at,
:updated_at,
:archived,
:visibility_level,
:last_activity_at,
:name_with_namespace,
:path_with_namespace
].each do |attr|
data[attr.to_s] = safely_read_attribute_for_elasticsearch(attr)
end
# Set it as a parent in our `project => child` JOIN field
data['join_field'] = es_type
# ES6 is now single-type per index, so we implement our own typing
data['type'] = 'project'
TRACKED_FEATURE_SETTINGS.each do |feature|
data[feature] = project_feature.public_send(feature) # rubocop:disable GitlabSecurity/PublicSend
end
data
end
def self.elastic_search(query, options: {})
options[:in] = %w(name^10 name_with_namespace^2 path_with_namespace path^9 description)
query_hash = basic_query_hash(options[:in], query)
filters = []
if options[:namespace_id]
filters << {
terms: {
namespace_id: [options[:namespace_id]].flatten
}
}
end
if options[:non_archived]
filters << {
terms: {
archived: [!options[:non_archived]].flatten
}
}
end
if options[:visibility_levels]
filters << {
terms: {
visibility_level: [options[:visibility_levels]].flatten
}
}
end
if options[:project_ids]
filters << {
bool: project_ids_query(options[:current_user], options[:project_ids], options[:public_and_internal_projects])
}
end
query_hash[:query][:bool][:filter] = filters
query_hash[:sort] = [:_score]
self.__elasticsearch__.search(query_hash)
end
def self.indexed_association_classes
INDEXED_ASSOCIATIONS.map do |association_name|
reflect_on_association(association_name).klass
end
end
def each_indexed_association
INDEXED_ASSOCIATIONS.each do |association_name|
association = self.association(association_name)
......
......@@ -11,7 +11,7 @@ module EE
WEIGHT_ANY = 'Any'.freeze
WEIGHT_NONE = 'None'.freeze
include Elastic::IssuesSearch
include Elastic::ApplicationVersionedSearch
scope :order_weight_desc, -> { reorder ::Gitlab::Database.nulls_last_order('weight', 'DESC') }
scope :order_weight_asc, -> { reorder ::Gitlab::Database.nulls_last_order('weight') }
......
......@@ -5,7 +5,7 @@ module EE
extend ActiveSupport::Concern
prepended do
include Elastic::NotesSearch
include Elastic::ApplicationVersionedSearch
end
end
end
......@@ -11,7 +11,7 @@ module EE
include FromUnion
prepended do
include Elastic::MergeRequestsSearch
include Elastic::ApplicationVersionedSearch
has_many :reviews, inverse_of: :merge_request
has_many :approvals, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
......
......@@ -5,7 +5,7 @@ module EE
extend ActiveSupport::Concern
prepended do
include Elastic::MilestonesSearch
include Elastic::ApplicationVersionedSearch
has_many :boards
end
......
......@@ -7,7 +7,7 @@ module EE
prepended do
include ::ObjectStorage::BackgroundMove
include Elastic::NotesSearch
include Elastic::ApplicationVersionedSearch
belongs_to :review, inverse_of: :notes
......
# frozen_string_literal: true
module Elastic
module Latest
class IssueClassProxy < ApplicationClassProxy
def nested?
true
end
def elastic_search(query, options: {})
query_hash =
if query =~ /#(\d+)\z/
iid_query_hash(Regexp.last_match(1))
else
basic_query_hash(%w(title^2 description), query)
end
options[:features] = 'issues'
query_hash = project_ids_filter(query_hash, options)
query_hash = confidentiality_filter(query_hash, options[:current_user])
search(query_hash)
end
private
def confidentiality_filter(query_hash, current_user)
return query_hash if current_user && current_user.full_private_access?
filter =
if current_user
{
bool: {
should: [
{ term: { confidential: false } },
{
bool: {
must: [
{ term: { confidential: true } },
{
bool: {
should: [
{ term: { author_id: current_user.id } },
{ term: { assignee_id: current_user.id } },
{ terms: { project_id: current_user.authorized_projects(Gitlab::Access::REPORTER).pluck_primary_key } }
]
}
}
]
}
}
]
}
}
else
{ term: { confidential: false } }
end
query_hash[:query][:bool][:filter] << filter
query_hash
end
end
end
end
# frozen_string_literal: true
module Elastic
module Latest
class IssueInstanceProxy < ApplicationInstanceProxy
def as_indexed_json(options = {})
data = {}
# We don't use as_json(only: ...) because it calls all virtual and serialized attributtes
# https://gitlab.com/gitlab-org/gitlab-ee/issues/349
[:id, :iid, :title, :description, :created_at, :updated_at, :state, :project_id, :author_id, :confidential].each do |attr|
data[attr.to_s] = safely_read_attribute_for_elasticsearch(attr)
end
data['assignee_id'] = safely_read_attribute_for_elasticsearch(:assignee_ids)
data.merge(generic_attributes)
end
end
end
end
# frozen_string_literal: true
module Elastic
module Latest
class MergeRequestClassProxy < ApplicationClassProxy
def nested?
true
end
def elastic_search(query, options: {})
query_hash =
if query =~ /\!(\d+)\z/
iid_query_hash(Regexp.last_match(1))
else
basic_query_hash(%w(title^2 description), query)
end
options[:features] = 'merge_requests'
query_hash = project_ids_filter(query_hash, options)
search(query_hash)
end
end
end
end
# frozen_string_literal: true
module Elastic
module MergeRequestsSearch
extend ActiveSupport::Concern
included do
include ApplicationSearch
module Latest
class MergeRequestInstanceProxy < ApplicationInstanceProxy
def as_indexed_json(options = {})
# We don't use as_json(only: ...) because it calls all virtual and serialized attributtes
# https://gitlab.com/gitlab-org/gitlab-ee/issues/349
......@@ -32,28 +28,6 @@ module Elastic
data.merge(generic_attributes)
end
def es_parent
"project_#{target_project_id}"
end
def self.nested?
true
end
def self.elastic_search(query, options: {})
query_hash =
if query =~ /\!(\d+)\z/
iid_query_hash(Regexp.last_match(1))
else
basic_query_hash(%w(title^2 description), query)
end
options[:features] = 'merge_requests'
query_hash = project_ids_filter(query_hash, options)
self.__elasticsearch__.search(query_hash)
end
end
end
end
# frozen_string_literal: true
module Elastic
module Latest
class MilestoneClassProxy < ApplicationClassProxy
def nested?
true
end
def elastic_search(query, options: {})
options[:in] = %w(title^2 description)
query_hash = basic_query_hash(options[:in], query)
query_hash = project_ids_filter(query_hash, options)
search(query_hash)
end
end
end
end
# frozen_string_literal: true
module Elastic
module MilestonesSearch
extend ActiveSupport::Concern
included do
include ApplicationSearch
module Latest
class MilestoneInstanceProxy < ApplicationInstanceProxy
def as_indexed_json(options = {})
# We don't use as_json(only: ...) because it calls all virtual and serialized attributtes
# https://gitlab.com/gitlab-org/gitlab-ee/issues/349
......@@ -18,20 +14,6 @@ module Elastic
data.merge(generic_attributes)
end
def self.nested?
true
end
def self.elastic_search(query, options: {})
options[:in] = %w(title^2 description)
query_hash = basic_query_hash(options[:in], query)
query_hash = project_ids_filter(query_hash, options)
self.__elasticsearch__.search(query_hash)
end
end
end
end
# frozen_string_literal: true
module Elastic
module NotesSearch
extend ActiveSupport::Concern
included do
include ApplicationSearch
module Latest
class NoteClassProxy < ApplicationClassProxy
def es_type
'note'
end
def as_indexed_json(options = {})
data = {}
# We don't use as_json(only: ...) because it calls all virtual and serialized attributtes
# https://gitlab.com/gitlab-org/gitlab-ee/issues/349
[:id, :note, :project_id, :noteable_type, :noteable_id, :created_at, :updated_at].each do |attr|
data[attr.to_s] = safely_read_attribute_for_elasticsearch(attr)
end
if noteable.is_a?(Issue)
data['issue'] = {
assignee_id: noteable.assignee_ids,
author_id: noteable.author_id,
confidential: noteable.confidential
}
end
data.merge(generic_attributes)
end
def self.nested?
def nested?
true
end
def self.elastic_search(query, options: {})
def elastic_search(query, options: {})
options[:in] = ['note']
query_hash = basic_query_hash(%w[note], query)
......@@ -49,11 +25,13 @@ module Elastic
query_hash[:highlight] = highlight_options(options[:in])
self.__elasticsearch__.search(query_hash)
search(query_hash)
end
def self.confidentiality_filter(query_hash, current_user)
return query_hash if current_user && current_user.full_private_access?
private
def confidentiality_filter(query_hash, current_user)
return query_hash if current_user&.full_private_access?
filter = {
bool: {
......@@ -74,7 +52,7 @@ module Elastic
should: [
{ term: { "issue.author_id" => current_user.id } },
{ term: { "issue.assignee_id" => current_user.id } },
{ terms: { "project_id" => current_user.authorized_projects(Gitlab::Access::REPORTER).pluck(:id) } }
{ terms: { "project_id" => current_user.authorized_projects(Gitlab::Access::REPORTER).pluck_primary_key } }
]
}
}
......
# frozen_string_literal: true
module Elastic
module Latest
class NoteInstanceProxy < ApplicationInstanceProxy
delegate :noteable, to: :target
def as_indexed_json(options = {})
data = {}
# We don't use as_json(only: ...) because it calls all virtual and serialized attributtes
# https://gitlab.com/gitlab-org/gitlab-ee/issues/349
[:id, :note, :project_id, :noteable_type, :noteable_id, :created_at, :updated_at].each do |attr|
data[attr.to_s] = safely_read_attribute_for_elasticsearch(attr)
end
if noteable.is_a?(Issue)
data['issue'] = {
assignee_id: noteable.assignee_ids,
author_id: noteable.author_id,
confidential: noteable.confidential
}
end
data.merge(generic_attributes)
end
end
end
end
# frozen_string_literal: true
module Elastic
module Latest
class ProjectClassProxy < ApplicationClassProxy
def elastic_search(query, options: {})
options[:in] = %w(name^10 name_with_namespace^2 path_with_namespace path^9 description)
query_hash = basic_query_hash(options[:in], query)
filters = []
if options[:namespace_id]
filters << {
terms: {
namespace_id: [options[:namespace_id]].flatten
}
}
end
if options[:non_archived]
filters << {
terms: {
archived: [!options[:non_archived]].flatten
}
}
end
if options[:visibility_levels]
filters << {
terms: {
visibility_level: [options[:visibility_levels]].flatten
}
}
end
if options[:project_ids]
filters << {
bool: project_ids_query(options[:current_user], options[:project_ids], options[:public_and_internal_projects])
}
end
query_hash[:query][:bool][:filter] = filters
query_hash[:sort] = [:_score]
search(query_hash)
end
end
end
end
# frozen_string_literal: true
module Elastic
module Latest
class ProjectInstanceProxy < ApplicationInstanceProxy
TRACKED_FEATURE_SETTINGS = %w(
issues_access_level
merge_requests_access_level
snippets_access_level
wiki_access_level
repository_access_level
).freeze
def as_indexed_json(options = {})
# We don't use as_json(only: ...) because it calls all virtual and serialized attributtes
# https://gitlab.com/gitlab-org/gitlab-ee/issues/349
data = {}
[
:id,
:name,
:path,
:description,
:namespace_id,
:created_at,
:updated_at,
:archived,
:visibility_level,
:last_activity_at,
:name_with_namespace,
:path_with_namespace
].each do |attr|
data[attr.to_s] = safely_read_attribute_for_elasticsearch(attr)
end
# Set it as a parent in our `project => child` JOIN field
data['join_field'] = es_type
# ES6 is now single-type per index, so we implement our own typing
data['type'] = 'project'
TRACKED_FEATURE_SETTINGS.each do |feature|
data[feature] = target.project_feature.public_send(feature) # rubocop:disable GitlabSecurity/PublicSend
end
data
end
end
end
end
# frozen_string_literal: true
module Elastic
module V12p1
IssueClassProxy = Elastic::Latest::IssueClassProxy
end
end
# frozen_string_literal: true
module Elastic
module V12p1
IssueInstanceProxy = Elastic::Latest::IssueInstanceProxy
end
end
# frozen_string_literal: true
module Elastic
module V12p1
MergeRequestClassProxy = Elastic::Latest::MergeRequestClassProxy
end
end
# frozen_string_literal: true
module Elastic
module V12p1
MergeRequestInstanceProxy = Elastic::Latest::MergeRequestInstanceProxy
end
end
# frozen_string_literal: true
module Elastic
module V12p1
MilestoneClassProxy = Elastic::Latest::MilestoneClassProxy
end
end
# frozen_string_literal: true
module Elastic
module V12p1
MilestoneInstanceProxy = Elastic::Latest::MilestoneInstanceProxy
end
end
# frozen_string_literal: true
module Elastic
module V12p1
NoteClassProxy = Elastic::Latest::NoteClassProxy
end
end
# frozen_string_literal: true
module Elastic
module V12p1
NoteInstanceProxy = Elastic::Latest::NoteInstanceProxy
end
end
# frozen_string_literal: true
module Elastic
module V12p1
ProjectClassProxy = Elastic::Latest::ProjectClassProxy
end
end
# frozen_string_literal: true
module Elastic
module V12p1
ProjectInstanceProxy = Elastic::Latest::ProjectInstanceProxy
end
end
......@@ -199,7 +199,7 @@ module Gitlab
def milestones
strong_memoize(:milestones) do
# Must pass 'issues' and 'merge_requests' to check
# if any of the features is available for projects in Elastic::ApplicationSearch#project_ids_query
# if any of the features is available for projects in ApplicationClassProxy#project_ids_query
# Otherwise it will ignore project_ids and return milestones
# from projects with milestones disabled.
options = base_options
......
......@@ -89,7 +89,7 @@ describe Issue, :elastic do
expected_hash['assignee_id'] = [assignee.id]
expect(issue.as_indexed_json).to eq(expected_hash)
expect(issue.__elasticsearch__.as_indexed_json).to eq(expected_hash)
end
it_behaves_like 'no results when the user cannot read cross project' do
......
......@@ -61,7 +61,7 @@ describe MergeRequest, :elastic do
'type' => merge_request.es_type
})
expect(merge_request.as_indexed_json).to eq(expected_hash)
expect(merge_request.__elasticsearch__.as_indexed_json).to eq(expected_hash)
end
it_behaves_like 'no results when the user cannot read cross project' do
......
......@@ -53,7 +53,7 @@ describe Milestone, :elastic do
'type' => milestone.es_type
})
expect(milestone.as_indexed_json).to eq(expected_hash)
expect(milestone.__elasticsearch__.as_indexed_json).to eq(expected_hash)
end
it_behaves_like 'no results when the user cannot read cross project' do
......
......@@ -88,7 +88,7 @@ describe Note, :elastic do
type
)
expect(note.as_indexed_json.keys).to eq(expected_hash_keys)
expect(note.__elasticsearch__.as_indexed_json.keys).to eq(expected_hash_keys)
end
it "does not create ElasticIndexerWorker job for system messages" do
......@@ -105,7 +105,7 @@ describe Note, :elastic do
Note.subclasses.each do |note_class|
expect(note_class.index_name).to eq(Note.index_name)
expect(note_class.document_type).to eq(Note.document_type)
expect(note_class.mappings.to_hash).to eq(Note.mappings.to_hash)
expect(note_class.__elasticsearch__.mappings.to_hash).to eq(Note.__elasticsearch__.mappings.to_hash)
end
end
......
......@@ -159,6 +159,6 @@ describe Project, :elastic do
expected_hash['name_with_namespace'] = project.full_name
expected_hash['path_with_namespace'] = project.full_path
expect(project.as_indexed_json).to eq(expected_hash)
expect(project.__elasticsearch__.as_indexed_json).to eq(expected_hash)
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment