• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nshkrdotcom / ElixirScope / ff4c6cc145dada56ff8992a50a8823a65ebf06b1

29 May 2025 03:21AM UTC coverage: 58.866% (-1.3%) from 60.215%
ff4c6cc145dada56ff8992a50a8823a65ebf06b1

push

github

NSHkr
refactor dfg_generator

559 of 849 new or added lines in 10 files covered. (65.84%)

26 existing lines in 4 files now uncovered.

5896 of 10016 relevant lines covered (58.87%)

3332.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.29
/lib/elixir_scope/ast_repository/query_builder.ex
1
defmodule ElixirScope.ASTRepository.QueryBuilder do
2
  @moduledoc """
3
  Advanced query builder for the Enhanced AST Repository.
4
  
5
  Provides powerful querying capabilities with:
6
  - Complex filters for complexity, patterns, dependencies
7
  - Query optimization using available indexes
8
  - Result caching and performance monitoring
9
  - Support for semantic, structural, performance, and security queries
10
  
11
  ## Query Types
12
  
13
  - **Semantic queries**: Find functions similar to a given one
14
  - **Structural queries**: Find specific AST patterns (e.g., GenServer implementations)
15
  - **Performance queries**: Find functions with specific complexity characteristics
16
  - **Security queries**: Identify potential security vulnerabilities
17
  - **Dependency queries**: Find modules using specific functions or patterns
18
  
19
  ## Performance Targets
20
  
21
  - Simple queries: <50ms
22
  - Complex queries: <200ms
23
  - Memory usage: <50MB for query execution
24
  
25
  ## Examples
26
  
27
      # Complex function query
28
      {:ok, functions} = QueryBuilder.execute_query(repo, %{
29
        select: [:module, :function, :complexity, :performance_profile],
30
        from: :functions,
31
        where: [
32
          {:complexity, :gt, 15},
33
          {:calls, :contains, {Ecto.Repo, :all, 1}},
34
          {:performance_profile, :not_nil}
35
        ],
36
        order_by: {:desc, :complexity},
37
        limit: 20
38
      })
39
      
40
      # Semantic similarity query
41
      {:ok, similar} = QueryBuilder.execute_query(repo, %{
42
        select: [:module, :function, :similarity_score],
43
        from: :functions,
44
        where: [
45
          {:similar_to, {MyModule, :my_function, 2}},
46
          {:similarity_threshold, 0.8}
47
        ],
48
        order_by: {:desc, :similarity_score}
49
      })
50
  """
51
  
52
  use GenServer
53
  require Logger
54
  
55
  alias ElixirScope.ASTRepository.QueryBuilder.{
56
    Types,
57
    Normalizer,
58
    Validator,
59
    Optimizer,
60
    Executor,
61
    Cache
62
  }
63
  
64
  defstruct [
65
    :cache_pid,
66
    :opts
67
  ]
68

69
  # GenServer API
70

71
  def start_link(opts \\ []) do
72
    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
33✔
73
  end
74

75
  def init(opts) do
76
    # Start the cache process
77
    {:ok, cache_pid} = Cache.start_link()
33✔
78

79
    state = %__MODULE__{
33✔
80
      cache_pid: cache_pid,
81
      opts: opts
82
    }
83

84
    Logger.info("QueryBuilder started with caching enabled")
33✔
85
    {:ok, state}
86
  end
87

88
  def handle_call({:execute_query, repo, query_spec}, _from, state) do
89
    start_time = System.monotonic_time(:millisecond)
7✔
90

91
    case execute_query_internal(repo, query_spec) do
7✔
92
      {:ok, result} ->
UNCOV
93
        end_time = System.monotonic_time(:millisecond)
×
UNCOV
94
        execution_time = end_time - start_time
×
95

96
        # Record performance
UNCOV
97
        Cache.record_performance(execution_time)
×
98

99
        # Add metadata to result
UNCOV
100
        result_with_metadata = add_execution_metadata(result, execution_time, query_spec)
×
101

UNCOV
102
        {:reply, {:ok, result_with_metadata}, state}
×
103

104
      error ->
105
        {:reply, error, state}
7✔
106
    end
107
  end
108

109
  def handle_call({:build_query, query_spec}, _from, state) do
110
    case build_query_internal(query_spec) do
26✔
111
      {:ok, query} ->
112
        {:reply, {:ok, query}, state}
16✔
113

114
      error ->
115
        {:reply, error, state}
10✔
116
    end
117
  end
118

119
  def handle_call(:get_cache_stats, _from, state) do
120
    case Cache.get_stats() do
2✔
121
      {:ok, stats} -> {:reply, {:ok, stats}, state}
2✔
UNCOV
122
      error -> {:reply, error, state}
×
123
    end
124
  end
125

126
  def handle_call(:clear_cache, _from, state) do
127
    case Cache.clear() do
34✔
128
      :ok -> {:reply, :ok, state}
34✔
UNCOV
129
      error -> {:reply, error, state}
×
130
    end
131
  end
132

133
  # Public API
134

135
  @doc """
136
  Builds a query structure from keyword options.
137
  
138
  ## Parameters
139
  
140
  - `query_spec` - Map or keyword list with query specifications
141
  
142
  ## Examples
143
  
144
      iex> QueryBuilder.build_query(%{
145
      ...>   select: [:module, :function, :complexity],
146
      ...>   from: :functions,
147
      ...>   where: [{:complexity, :gt, 10}],
148
      ...>   order_by: {:desc, :complexity},
149
      ...>   limit: 20
150
      ...> })
151
      {:ok, %Types{...}}
152
  """
153
  @spec build_query(map() | keyword()) :: {:ok, Types.query_t()} | {:error, term()}
154
  def build_query(query_spec) do
155
    GenServer.call(__MODULE__, {:build_query, query_spec})
26✔
156
  end
157

158
  @doc """
159
  Executes a query against the Enhanced Repository with optimization.
160
  
161
  ## Parameters
162
  
163
  - `repo` - The Enhanced Repository process
164
  - `query_spec` - Query specification map or QueryBuilder struct
165
  
166
  ## Returns
167
  
168
  - `{:ok, query_result()}` - Successful execution with results and metadata
169
  - `{:error, term()}` - Error during execution
170
  """
171
  @spec execute_query(pid() | atom(), map() | Types.query_t()) :: {:ok, Types.query_result()} | {:error, term()}
172
  def execute_query(repo, query_spec) do
173
    GenServer.call(__MODULE__, {:execute_query, repo, query_spec}, 30_000)
7✔
174
  end
175

176
  @doc """
177
  Gets cache statistics for monitoring performance.
178
  """
179
  @spec get_cache_stats() :: {:ok, map()}
180
  def get_cache_stats() do
181
    GenServer.call(__MODULE__, :get_cache_stats)
2✔
182
  end
183

184
  @doc """
185
  Clears the query cache.
186
  """
187
  @spec clear_cache() :: :ok
188
  def clear_cache() do
189
    GenServer.call(__MODULE__, :clear_cache)
34✔
190
  end
191

192
  # Public functions for testing and direct access
193

194
  @doc false
195
  def evaluate_condition(item, condition) do
196
    Executor.evaluate_condition(item, condition)
34✔
197
  end
198

199
  @doc false
200
  def apply_ordering(data, order_spec) do
201
    Executor.apply_ordering(data, order_spec)
3✔
202
  end
203

204
  @doc false
205
  def apply_limit_offset(data, limit, offset) do
206
    Executor.apply_limit_offset(data, limit, offset)
3✔
207
  end
208

209
  @doc false
210
  def apply_select(data, fields) do
211
    Executor.apply_select(data, fields)
2✔
212
  end
213

214
  # Private Implementation
215

216
  defp execute_query_internal(repo, query_spec) do
217
    with {:ok, query} <- Normalizer.normalize_query(query_spec),
7✔
218
         {:ok, validated_query} <- Validator.validate_query(query),
7✔
219
         {:ok, optimized_query} <- Optimizer.optimize_query(validated_query),
7✔
220
         {:ok, result} <- execute_optimized_query(repo, optimized_query) do
7✔
221
      {:ok, result}
222
    else
223
      error -> error
7✔
224
    end
225
  end
226

227
  defp build_query_internal(query_spec) do
228
    with {:ok, query} <- Normalizer.normalize_query(query_spec),
26✔
229
         {:ok, validated_query} <- Validator.validate_query(query),
23✔
230
         {:ok, optimized_query} <- Optimizer.optimize_query(validated_query) do
16✔
231
      {:ok, optimized_query}
232
    else
233
      error -> error
10✔
234
    end
235
  end
236

237
  defp execute_optimized_query(repo, %Types{} = query) do
238
    # Check cache first if we have a cache key
239
    case query.cache_key do
7✔
240
      nil ->
241
        # No cache key, execute directly
UNCOV
242
        execute_against_repo(repo, query)
×
243

244
      cache_key ->
245
        case Cache.get(cache_key) do
7✔
UNCOV
246
          {:hit, cached_result} ->
×
247
            {:ok, Map.put(cached_result, :cache_hit, true)}
248

249
          :miss ->
250
            case execute_against_repo(repo, query) do
7✔
251
              {:ok, result} ->
252
                # Cache the result
UNCOV
253
                Cache.put(cache_key, result)
×
254
                {:ok, Map.put(result, :cache_hit, false)}
255

256
              error -> error
7✔
257
            end
258
        end
259
    end
260
  end
261

262
  defp execute_against_repo(repo, %Types{} = query) do
263
    Executor.execute_query(repo, query)
7✔
264
  end
265

266
  defp add_execution_metadata(result, execution_time, query_spec) do
267
    # Determine performance score based on execution time
UNCOV
268
    performance_score = cond do
×
UNCOV
269
      execution_time <= Types.simple_query_threshold() -> :excellent
×
UNCOV
270
      execution_time <= Types.complex_query_threshold() -> :good
×
UNCOV
271
      execution_time <= Types.complex_query_threshold() * 2 -> :fair
×
UNCOV
272
      true -> :poor
×
273
    end
274

275
    # Extract optimization hints and estimated cost from query if it's a Types struct
UNCOV
276
    {optimization_hints, estimated_cost} = case query_spec do
×
UNCOV
277
      %Types{optimization_hints: hints, estimated_cost: cost} -> {hints || [], cost}
×
UNCOV
278
      _ -> {[], nil}
×
279
    end
280

UNCOV
281
    metadata = %{
×
282
      execution_time_ms: execution_time,
283
      cache_hit: Map.get(result, :cache_hit, false),
284
      optimization_applied: optimization_hints,
285
      performance_score: performance_score,
286
      estimated_cost: estimated_cost
287
    }
288

UNCOV
289
    Map.put(result, :metadata, metadata)
×
290
  end
291
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc