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

nshkrdotcom / ElixirScope / 671f6c2f62938b306383bc0eb80a3fa9d3d150e6

28 May 2025 06:03PM UTC coverage: 59.97% (-0.05%) from 60.023%
671f6c2f62938b306383bc0eb80a3fa9d3d150e6

push

github

NSHkr
CPG formalization plans

5675 of 9463 relevant lines covered (59.97%)

3796.94 hits per line

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

59.56
/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.EnhancedRepository
56
  alias ElixirScope.ASTRepository.Enhanced.{
57
    EnhancedFunctionData,
58
    EnhancedModuleData
59
  }
60
  
61
  @table_name :query_cache
62
  @index_table :query_indexes
63
  @performance_table :query_performance
64
  
65
  # Query cache TTL in milliseconds (5 minutes)
66
  @cache_ttl 300_000
67
  
68
  # Performance thresholds
69
  @simple_query_threshold 50
70
  @complex_query_threshold 200
71
  
72
  defstruct [
73
    :select,
74
    :from,
75
    :where,
76
    :order_by,
77
    :limit,
78
    :offset,
79
    :group_by,
80
    :having,
81
    :joins,
82
    :cache_key,
83
    :estimated_cost,
84
    :optimization_hints
85
  ]
86
  
87
  @type query_t :: %__MODULE__{
88
    select: list(atom()) | :all,
89
    from: :functions | :modules | :patterns,
90
    where: list(filter_condition()),
91
    order_by: {atom(), :asc | :desc} | list({atom(), :asc | :desc}),
92
    limit: pos_integer() | nil,
93
    offset: non_neg_integer() | nil,
94
    group_by: list(atom()) | nil,
95
    having: list(filter_condition()) | nil,
96
    joins: list(join_spec()) | nil,
97
    cache_key: String.t() | nil,
98
    estimated_cost: non_neg_integer() | nil,
99
    optimization_hints: list(String.t()) | nil
100
  }
101
  
102
  @type filter_condition :: 
103
    {atom(), :eq | :ne | :gt | :lt | :gte | :lte | :in | :not_in | :contains | :not_contains | :matches | :similar_to, any()} |
104
    {:and, list(filter_condition())} |
105
    {:or, list(filter_condition())} |
106
    {:not, filter_condition()}
107
  
108
  @type join_spec :: {atom(), atom(), atom(), atom()}
109
  
110
  @type query_result :: %{
111
    data: list(map()),
112
    metadata: %{
113
      total_count: non_neg_integer(),
114
      execution_time_ms: non_neg_integer(),
115
      cache_hit: boolean(),
116
      optimization_applied: list(String.t()),
117
      performance_score: :excellent | :good | :fair | :poor
118
    }
119
  }
120
  
121
  # GenServer API
122
  
123
  def start_link(opts \\ []) do
124
    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
33✔
125
  end
126
  
127
  def init(opts) do
128
    # Create ETS tables for caching and indexing
129
    :ets.new(@table_name, [:named_table, :public, :set, {:read_concurrency, true}])
32✔
130
    :ets.new(@index_table, [:named_table, :public, :bag, {:read_concurrency, true}])
32✔
131
    :ets.new(@performance_table, [:named_table, :public, :set, {:read_concurrency, true}])
32✔
132
    
133
    # Schedule cache cleanup
134
    Process.send_after(self(), :cleanup_cache, @cache_ttl)
32✔
135
    
136
    state = %{
32✔
137
      cache_stats: %{hits: 0, misses: 0},
138
      query_stats: %{total: 0, avg_time: 0},
139
      opts: opts
140
    }
141
    
142
    Logger.info("QueryBuilder started with caching enabled")
32✔
143
    {:ok, state}
144
  end
145
  
146
  def handle_info(:cleanup_cache, state) do
147
    cleanup_expired_cache()
×
148
    Process.send_after(self(), :cleanup_cache, @cache_ttl)
×
149
    {:noreply, state}
150
  end
151
  
152
  def handle_call({:execute_query, repo, query_spec}, _from, state) do
153
    start_time = System.monotonic_time(:millisecond)
7✔
154
    
155
    case execute_query_internal(repo, query_spec) do
7✔
156
      {:ok, result} ->
157
        end_time = System.monotonic_time(:millisecond)
×
158
        execution_time = end_time - start_time
×
159
        
160
        # Update performance stats
161
        updated_state = update_performance_stats(state, execution_time)
×
162
        
163
        # Add metadata to result
164
        result_with_metadata = add_execution_metadata(result, execution_time, query_spec)
×
165
        
166
        {:reply, {:ok, result_with_metadata}, updated_state}
×
167
      
168
      error ->
169
        {:reply, error, state}
7✔
170
    end
171
  end
172
  
173
  def handle_call({:build_query, query_spec}, _from, state) do
174
    case build_query_internal(query_spec) do
26✔
175
      {:ok, query} ->
176
        {:reply, {:ok, query}, state}
16✔
177
      
178
      error ->
179
        {:reply, error, state}
10✔
180
    end
181
  end
182
  
183
  def handle_call(:get_cache_stats, _from, state) do
184
    {:reply, {:ok, state.cache_stats}, state}
2✔
185
  end
186
  
187
  def handle_call(:clear_cache, _from, state) do
188
    :ets.delete_all_objects(@table_name)
33✔
189
    updated_stats = %{state.cache_stats | hits: 0, misses: 0}
33✔
190
    {:reply, :ok, %{state | cache_stats: updated_stats}}
33✔
191
  end
192
  
193
  # Public API
194
  
195
  @doc """
196
  Builds a query structure from keyword options.
197
  
198
  ## Parameters
199
  
200
  - `query_spec` - Map or keyword list with query specifications
201
  
202
  ## Examples
203
  
204
      iex> QueryBuilder.build_query(%{
205
      ...>   select: [:module, :function, :complexity],
206
      ...>   from: :functions,
207
      ...>   where: [{:complexity, :gt, 10}],
208
      ...>   order_by: {:desc, :complexity},
209
      ...>   limit: 20
210
      ...> })
211
      {:ok, %QueryBuilder{...}}
212
  """
213
  @spec build_query(map() | keyword()) :: {:ok, query_t()} | {:error, term()}
214
  def build_query(query_spec) do
215
    GenServer.call(__MODULE__, {:build_query, query_spec})
26✔
216
  end
217
  
218
  @doc """
219
  Executes a query against the Enhanced Repository with optimization.
220
  
221
  ## Parameters
222
  
223
  - `repo` - The Enhanced Repository process
224
  - `query_spec` - Query specification map or QueryBuilder struct
225
  
226
  ## Returns
227
  
228
  - `{:ok, query_result()}` - Successful execution with results and metadata
229
  - `{:error, term()}` - Error during execution
230
  """
231
  @spec execute_query(pid() | atom(), map() | query_t()) :: {:ok, query_result()} | {:error, term()}
232
  def execute_query(repo, query_spec) do
233
    GenServer.call(__MODULE__, {:execute_query, repo, query_spec}, 30_000)
7✔
234
  end
235
  
236
  @doc """
237
  Gets cache statistics for monitoring performance.
238
  """
239
  @spec get_cache_stats() :: {:ok, map()}
240
  def get_cache_stats() do
241
    GenServer.call(__MODULE__, :get_cache_stats)
2✔
242
  end
243
  
244
  @doc """
245
  Clears the query cache.
246
  """
247
  @spec clear_cache() :: :ok
248
  def clear_cache() do
249
    GenServer.call(__MODULE__, :clear_cache)
34✔
250
  end
251
  
252
  # Public functions for testing
253
  
254
  @doc false
255
  def evaluate_condition(item, condition) do
256
    evaluate_condition_internal(item, condition)
34✔
257
  end
258
  
259
  @doc false
260
  def apply_ordering(data, order_spec) do
261
    apply_ordering_internal(data, order_spec)
3✔
262
  end
263
  
264
  @doc false
265
  def apply_limit_offset(data, limit, offset) do
266
    apply_limit_offset_internal(data, limit, offset)
3✔
267
  end
268
  
269
  @doc false
270
  def apply_select(data, fields) do
271
    apply_select_internal(data, fields)
×
272
  end
273
  
274
  # Private Implementation
275
  
276
  defp execute_query_internal(repo, query_spec) do
277
    with {:ok, query} <- normalize_query(query_spec),
7✔
278
         {:ok, optimized_query} <- optimize_query(query),
7✔
279
         {:ok, result} <- execute_optimized_query(repo, optimized_query) do
7✔
280
      {:ok, result}
281
    else
282
      error -> error
7✔
283
    end
284
  end
285
  
286
  defp build_query_internal(query_spec) do
287
    with {:ok, query} <- normalize_query(query_spec),
26✔
288
         {:ok, validated_query} <- validate_query(query),
23✔
289
         {:ok, optimized_query} <- optimize_query(validated_query) do
16✔
290
      {:ok, optimized_query}
291
    else
292
      error -> error
10✔
293
    end
294
  end
295
  
296
  defp normalize_query(query_spec) when is_map(query_spec) do
297
    query = %__MODULE__{
30✔
298
      select: Map.get(query_spec, :select, :all),
299
      from: Map.get(query_spec, :from, :functions),
300
      where: Map.get(query_spec, :where, []),
301
      order_by: normalize_order_by(Map.get(query_spec, :order_by)),
302
      limit: Map.get(query_spec, :limit),
303
      offset: Map.get(query_spec, :offset, 0),
304
      group_by: Map.get(query_spec, :group_by),
305
      having: Map.get(query_spec, :having),
306
      joins: Map.get(query_spec, :joins)
307
    }
308
    
309
    {:ok, query}
310
  end
311
  
312
  defp normalize_order_by(nil), do: nil
22✔
313
  defp normalize_order_by(order_by) when is_list(order_by) do
314
    # Convert keyword list to list of tuples if needed
315
    Enum.map(order_by, fn
2✔
316
      {field, direction} -> {field, direction}
5✔
317
      other -> other
×
318
    end)
319
  end
320
  defp normalize_order_by(order_by), do: order_by
6✔
321
  
322
  defp normalize_query(%__MODULE__{} = query), do: {:ok, query}
×
323
  defp normalize_query(_), do: {:error, :invalid_query_format}
3✔
324
  
325
  defp validate_query(%__MODULE__{} = query) do
326
    with :ok <- validate_from_clause(query.from),
23✔
327
         :ok <- validate_select_clause(query.select),
22✔
328
         :ok <- validate_where_clause(query.where),
21✔
329
         :ok <- validate_order_by_clause(query.order_by) do
17✔
330
      {:ok, query}
331
    else
332
      error -> error
7✔
333
    end
334
  end
335
  
336
  defp validate_from_clause(from) when from in [:functions, :modules, :patterns], do: :ok
22✔
337
  defp validate_from_clause(_), do: {:error, :invalid_from_clause}
1✔
338
  
339
  defp validate_select_clause(:all), do: :ok
17✔
340
  defp validate_select_clause(fields) when is_list(fields), do: :ok
4✔
341
  defp validate_select_clause(_), do: {:error, :invalid_select_clause}
1✔
342
  
343
  defp validate_where_clause(conditions) when is_list(conditions) do
344
    if Enum.all?(conditions, &valid_condition?/1) do
21✔
345
      :ok
346
    else
347
      {:error, :invalid_where_condition}
348
    end
349
  end
350
  
351
  defp validate_order_by_clause(nil), do: :ok
9✔
352
  defp validate_order_by_clause({field, direction}) when is_atom(field) and direction in [:asc, :desc], do: :ok
×
353
  defp validate_order_by_clause({direction, field}) when is_atom(field) and direction in [:asc, :desc], do: :ok
5✔
354
  defp validate_order_by_clause(list) when is_list(list) do
355
    if Enum.all?(list, fn 
2✔
356
      {field, direction} when is_atom(field) and direction in [:asc, :desc] -> true
5✔
357
      {direction, field} when is_atom(field) and direction in [:asc, :desc] -> true
×
358
      _ -> false
×
359
    end) do
360
      :ok
361
    else
362
      {:error, :invalid_order_by_clause}
363
    end
364
  end
365
  defp validate_order_by_clause(_), do: {:error, :invalid_order_by_clause}
1✔
366
  
367
  defp valid_condition?({field, op, _value}) when is_atom(field) do
368
    op in [:eq, :ne, :gt, :lt, :gte, :lte, :in, :not_in, :contains, :not_contains, :matches, :similar_to, :not_nil, :nil]
30✔
369
  end
370
  defp valid_condition?({field, op}) when is_atom(field) and op in [:not_nil, :nil] do
2✔
371
    # Support 2-tuple format for operators that don't need a value
372
    true
373
  end
374
  defp valid_condition?({op, _value}) when op in [:similar_to, :matches] do
6✔
375
    # Handle special cases like {:similar_to, {module, function, arity}}
376
    true
377
  end
378
  defp valid_condition?({special_field, _value}) when special_field in [:similarity_threshold] do
1✔
379
    # Handle special query parameters
380
    true
381
  end
382
  defp valid_condition?({:and, conditions}) when is_list(conditions) do
383
    Enum.all?(conditions, fn condition ->
2✔
384
      valid_condition?(condition)
4✔
385
    end)
386
  end
387
  defp valid_condition?({:or, conditions}) when is_list(conditions) do
388
    Enum.all?(conditions, &valid_condition?/1)
3✔
389
  end
390
  defp valid_condition?({:not, condition}) do
391
    valid_condition?(condition)
2✔
392
  end
393
  defp valid_condition?(_), do: false
2✔
394
  
395
  defp optimize_query(%__MODULE__{} = query) do
396
    optimized_query = query
23✔
397
    |> add_cache_key()
398
    |> estimate_cost()
399
    |> generate_optimization_hints()
400
    |> apply_optimizations()
401
    
402
    {:ok, optimized_query}
403
  end
404
  
405
  defp add_cache_key(%__MODULE__{} = query) do
406
    cache_key = :crypto.hash(:md5, :erlang.term_to_binary(query)) |> Base.encode16()
23✔
407
    %{query | cache_key: cache_key}
23✔
408
  end
409
  
410
  defp estimate_cost(%__MODULE__{} = query) do
411
    base_cost = case query.from do
23✔
412
      :functions -> 60  # Increased from 50
22✔
413
      :modules -> 35    # Increased from 25
1✔
414
      :patterns -> 120  # Increased from 100
×
415
    end
416
    
417
    # Adjust for where conditions
418
    where_cost = length(query.where || []) * 20  # Increased from 15
23✔
419
    
420
    # Adjust for joins
421
    join_cost = length(query.joins || []) * 40  # Increased from 30
23✔
422
    
423
    # Adjust for complex operations
424
    complex_ops_count = count_complex_operations(query.where || [])
23✔
425
    complex_cost = complex_ops_count * 80  # Increased from 75
23✔
426
    
427
    # Adjust for order_by complexity
428
    order_cost = case query.order_by do
23✔
429
      nil -> 0
16✔
430
      list when is_list(list) -> length(list) * 15  # Increased from 10
2✔
431
      _ -> 8  # Increased from 5
5✔
432
    end
433
    
434
    total_cost = base_cost + where_cost + join_cost + complex_cost + order_cost
23✔
435
    
436
    %{query | estimated_cost: total_cost}
23✔
437
  end
438
  
439
  defp count_complex_operations(conditions) do
440
    Enum.reduce(conditions, 0, fn condition, acc ->
32✔
441
      case condition do
54✔
442
        {_field, :similar_to, _value} -> 
443
          acc + 1
×
444
        {:similar_to, _value} -> 
445
          acc + 1
4✔
446
        {_field, :matches, _value} -> 
447
          acc + 1
1✔
448
        {:matches, _value} -> 
449
          acc + 1
4✔
450
        {:and, sub_conditions} -> 
451
          acc + count_complex_operations(sub_conditions)
3✔
452
        {:or, sub_conditions} -> 
453
          acc + count_complex_operations(sub_conditions)
4✔
454
        {:not, condition} -> 
455
          acc + count_complex_operations([condition])
2✔
456
        _ -> 
457
          acc
36✔
458
      end
459
    end)
460
  end
461
  
462
  defp generate_optimization_hints(%__MODULE__{} = query) do
463
    hints = []
23✔
464
    
465
    # Suggest adding limits for large result sets
466
    hints = if is_nil(query.limit) and query.estimated_cost > 80 do
23✔
467
      ["Consider adding a LIMIT clause to reduce memory usage" | hints]
468
    else
469
      hints
13✔
470
    end
471
    
472
    # Suggest indexing for complex where conditions (lowered from > 3 to >= 3)
473
    hints = if length(query.where || []) >= 3 do
23✔
474
      ["Complex WHERE conditions detected - ensure proper indexing" | hints]
475
    else
476
      hints
16✔
477
    end
478
    
479
    # Suggest caching for expensive queries (lowered from > 200 to > 120)
480
    hints = if query.estimated_cost > 120 do
23✔
481
      ["High-cost query detected - results will be cached" | hints]
482
    else
483
      hints
15✔
484
    end
485
    
486
    %{query | optimization_hints: hints}
23✔
487
  end
488
  
489
  defp apply_optimizations(%__MODULE__{} = query) do
490
    query
491
    |> apply_index_optimization()
492
    |> apply_limit_optimization()
493
    |> apply_order_optimization()
23✔
494
  end
495
  
496
  defp apply_index_optimization(%__MODULE__{} = query) do
497
    # Reorder WHERE conditions to use most selective filters first
498
    optimized_where = optimize_where_conditions(query.where || [])
23✔
499
    %{query | where: optimized_where}
23✔
500
  end
501
  
502
  defp apply_limit_optimization(%__MODULE__{} = query) do
503
    # Add default limit for expensive queries without one
504
    if is_nil(query.limit) and query.estimated_cost > 120 do
23✔
505
      %{query | limit: 1000}
6✔
506
    else
507
      query
17✔
508
    end
509
  end
510
  
511
  defp apply_order_optimization(%__MODULE__{} = query) do
512
    # Optimize ORDER BY to use available indexes
513
    query
23✔
514
  end
515
  
516
  defp optimize_where_conditions(conditions) do
517
    # Sort conditions by selectivity (most selective first)
518
    Enum.sort(conditions, &condition_selectivity/2)
23✔
519
  end
520
  
521
  defp condition_selectivity({_field1, op1, _value1}, {_field2, op2, _value2}) do
522
    selectivity_score(op1) >= selectivity_score(op2)
8✔
523
  end
524
  
525
  defp condition_selectivity({:and, _conditions1}, {_field2, op2, _value2}) do
526
    selectivity_score(:and) >= selectivity_score(op2)
×
527
  end
528
  
529
  defp condition_selectivity({_field1, op1, _value1}, {:and, _conditions2}) do
530
    selectivity_score(op1) >= selectivity_score(:and)
×
531
  end
532
  
533
  defp condition_selectivity({:or, _conditions1}, {_field2, op2, _value2}) do
534
    selectivity_score(:or) >= selectivity_score(op2)
1✔
535
  end
536
  
537
  defp condition_selectivity({_field1, op1, _value1}, {:or, _conditions2}) do
538
    selectivity_score(op1) >= selectivity_score(:or)
×
539
  end
540
  
541
  defp condition_selectivity({:not, _condition1}, {_field2, op2, _value2}) do
542
    selectivity_score(:not) >= selectivity_score(op2)
×
543
  end
544
  
545
  defp condition_selectivity({_field1, op1, _value1}, {:not, _condition2}) do
546
    selectivity_score(op1) >= selectivity_score(:not)
×
547
  end
548
  
549
  defp condition_selectivity({:and, _conditions1}, {:or, _conditions2}) do
550
    selectivity_score(:and) >= selectivity_score(:or)
3✔
551
  end
552
  
553
  defp condition_selectivity({:or, _conditions1}, {:and, _conditions2}) do
554
    selectivity_score(:or) >= selectivity_score(:and)
×
555
  end
556
  
557
  defp condition_selectivity({:and, _conditions1}, {:and, _conditions2}) do
×
558
    true  # Equal selectivity
559
  end
560
  
561
  defp condition_selectivity({:or, _conditions1}, {:or, _conditions2}) do
×
562
    true  # Equal selectivity
563
  end
564
  
565
  defp condition_selectivity({:not, _condition1}, {:not, _condition2}) do
×
566
    true  # Equal selectivity
567
  end
568
  
569
  defp condition_selectivity(_condition1, _condition2) do
10✔
570
    true  # Default case for unknown conditions
571
  end
572
  
573
  defp selectivity_score(:eq), do: 10
3✔
574
  defp selectivity_score(:in), do: 8
2✔
575
  defp selectivity_score(:gt), do: 6
5✔
576
  defp selectivity_score(:lt), do: 6
1✔
577
  defp selectivity_score(:gte), do: 5
×
578
  defp selectivity_score(:lte), do: 5
×
579
  defp selectivity_score(:contains), do: 4
2✔
580
  defp selectivity_score(:matches), do: 3
2✔
581
  defp selectivity_score(:similar_to), do: 2
×
582
  defp selectivity_score(:ne), do: 1
2✔
583
  defp selectivity_score(:and), do: 7  # AND is quite selective
3✔
584
  defp selectivity_score(:or), do: 3   # OR is less selective
4✔
585
  defp selectivity_score(:not), do: 2  # NOT is moderately selective
×
586
  defp selectivity_score(_), do: 0
×
587
  
588
  defp execute_optimized_query(repo, %__MODULE__{} = query) do
589
    # Check cache first
590
    case check_cache(query.cache_key) do
7✔
591
      {:hit, cached_result} ->
×
592
        {:ok, Map.put(cached_result, :cache_hit, true)}
593
      
594
      :miss ->
595
        case execute_query_against_repo(repo, query) do
7✔
596
          {:ok, result} ->
597
            # Cache the result
598
            cache_result(query.cache_key, result)
×
599
            {:ok, Map.put(result, :cache_hit, false)}
600
          
601
          error -> error
7✔
602
        end
603
    end
604
  end
605
  
606
  defp execute_query_against_repo(repo, %__MODULE__{} = query) do
607
    case query.from do
7✔
608
      :functions -> execute_function_query(repo, query)
7✔
609
      :modules -> execute_module_query(repo, query)
×
610
      :patterns -> execute_pattern_query(repo, query)
×
611
    end
612
  end
613
  
614
  defp execute_function_query(repo, %__MODULE__{} = query) do
615
    # Get all functions from repository
616
    case get_all_functions(repo) do
7✔
617
      {:ok, functions} ->
618
        filtered_functions = apply_where_filters(functions, query.where || [])
×
619
        ordered_functions = apply_ordering(filtered_functions, query.order_by)
×
620
        limited_functions = apply_limit_offset(ordered_functions, query.limit, query.offset || 0)
×
621
        selected_data = apply_select(limited_functions, query.select)
×
622
        
623
        result = %{
×
624
          data: selected_data,
625
          total_count: length(filtered_functions)
626
        }
627
        
628
        {:ok, result}
629
      
630
      error -> error
7✔
631
    end
632
  end
633
  
634
  defp execute_module_query(repo, %__MODULE__{} = query) do
635
    case get_all_modules(repo) do
×
636
      {:ok, modules} ->
637
        filtered_modules = apply_where_filters(modules, query.where || [])
×
638
        ordered_modules = apply_ordering(filtered_modules, query.order_by)
×
639
        limited_modules = apply_limit_offset(ordered_modules, query.limit, query.offset || 0)
×
640
        selected_data = apply_select(limited_modules, query.select)
×
641
        
642
        result = %{
×
643
          data: selected_data,
644
          total_count: length(filtered_modules)
645
        }
646
        
647
        {:ok, result}
648
      
649
      error -> error
×
650
    end
651
  end
652
  
653
  defp execute_pattern_query(_repo, %__MODULE__{} = _query) do
×
654
    # Pattern queries will be handled by PatternMatcher
655
    {:error, :pattern_queries_not_implemented}
656
  end
657
  
658
  defp get_all_functions(repo) do
659
    # Validate repository before attempting to query
660
    case validate_repository(repo) do
7✔
661
      :ok ->
×
662
        # This would integrate with the Enhanced Repository to get all functions
663
        # For now, return a placeholder for valid repositories
664
        {:ok, []}
665
      error -> error
7✔
666
    end
667
  end
668
  
669
  defp get_all_modules(repo) do
670
    # Validate repository before attempting to query
671
    case validate_repository(repo) do
×
672
      :ok ->
×
673
        # This would integrate with the Enhanced Repository to get all modules
674
        # For now, return a placeholder for valid repositories
675
        {:ok, []}
676
      error -> error
×
677
    end
678
  end
679
  
680
  defp validate_repository(repo) when is_pid(repo) do
681
    # Check if the process is alive
682
    if Process.alive?(repo) do
×
683
      :ok
684
    else
685
      {:error, :repository_not_available}
686
    end
687
  end
688
  
689
  defp validate_repository(repo) when is_atom(repo) do
690
    # Check if it's a registered process
691
    case Process.whereis(repo) do
7✔
692
      nil -> {:error, :repository_not_found}
7✔
693
      pid when is_pid(pid) -> 
694
        if Process.alive?(pid) do
×
695
          :ok
696
        else
697
          {:error, :repository_not_available}
698
        end
699
    end
700
  end
701
  
702
  defp validate_repository(_repo) do
×
703
    {:error, :invalid_repository}
704
  end
705
  
706
  defp apply_where_filters(data, []), do: data
×
707
  defp apply_where_filters(data, conditions) do
708
    Enum.filter(data, fn item ->
×
709
      evaluate_conditions(item, conditions)
×
710
    end)
711
  end
712
  
713
  defp evaluate_conditions(item, conditions) do
714
    Enum.all?(conditions, fn condition ->
×
715
      evaluate_condition(item, condition)
×
716
    end)
717
  end
718
  
719
  defp evaluate_condition_internal(item, {field, :eq, value}) do
720
    Map.get(item, field) == value
8✔
721
  end
722
  
723
  defp evaluate_condition_internal(item, {field, :ne, value}) do
724
    Map.get(item, field) != value
2✔
725
  end
726
  
727
  defp evaluate_condition_internal(item, {field, :gt, value}) do
728
    case Map.get(item, field) do
6✔
729
      nil -> false
×
730
      item_value -> item_value > value
6✔
731
    end
732
  end
733
  
734
  defp evaluate_condition_internal(item, {field, :lt, value}) do
735
    case Map.get(item, field) do
2✔
736
      nil -> false
×
737
      item_value -> item_value < value
2✔
738
    end
739
  end
740
  
741
  defp evaluate_condition_internal(item, {field, :gte, value}) do
742
    case Map.get(item, field) do
3✔
743
      nil -> false
×
744
      item_value -> item_value >= value
3✔
745
    end
746
  end
747
  
748
  defp evaluate_condition_internal(item, {field, :lte, value}) do
749
    case Map.get(item, field) do
3✔
750
      nil -> false
×
751
      item_value -> item_value <= value
3✔
752
    end
753
  end
754
  
755
  defp evaluate_condition_internal(item, {field, :in, values}) when is_list(values) do
756
    case Map.get(item, field) do
2✔
757
      list when is_list(list) -> 
758
        # Check if any value in the field list is in the condition values
759
        Enum.any?(list, &(&1 in values))
2✔
760
      single_value -> 
761
        # Check if the single field value is in the condition values
762
        single_value in values
×
763
    end
764
  end
765
  
766
  defp evaluate_condition_internal(item, {field, :not_in, values}) when is_list(values) do
767
    case Map.get(item, field) do
2✔
768
      list when is_list(list) -> 
769
        # Check if no value in the field list is in the condition values
770
        not Enum.any?(list, &(&1 in values))
2✔
771
      single_value -> 
772
        # Check if the single field value is not in the condition values
773
        single_value not in values
×
774
    end
775
  end
776
  
777
  defp evaluate_condition_internal(item, {field, :contains, value}) do
778
    case Map.get(item, field) do
6✔
779
      list when is_list(list) -> value in list
2✔
780
      string when is_binary(string) -> String.contains?(string, to_string(value))
4✔
781
      _ -> false
×
782
    end
783
  end
784
  
785
  defp evaluate_condition_internal(item, {field, :not_contains, value}) do
786
    not evaluate_condition_internal(item, {field, :contains, value})
2✔
787
  end
788
  
789
  defp evaluate_condition_internal(item, {field, :matches, pattern}) do
790
    case Map.get(item, field) do
4✔
791
      string when is_binary(string) ->
792
        case Regex.compile(pattern) do
4✔
793
          {:ok, regex} -> Regex.match?(regex, string)
3✔
794
          _ -> false
1✔
795
        end
796
      _ -> false
×
797
    end
798
  end
799
  
800
  defp evaluate_condition_internal(item, {field, :similar_to, {module, function, arity}}) do
801
    # Placeholder for semantic similarity - would integrate with AI analysis
802
    case Map.get(item, field) do
×
803
      {^module, ^function, ^arity} -> true
×
804
      _ -> false
×
805
    end
806
  end
807
  
808
  defp evaluate_condition_internal(item, {field, :not_nil, _value}) do
809
    Map.get(item, field) != nil
×
810
  end
811
  
812
  defp evaluate_condition_internal(item, {field, :not_nil}) do
813
    Map.get(item, field) != nil
×
814
  end
815
  
816
  defp evaluate_condition_internal(item, {field, :nil, _value}) do
817
    Map.get(item, field) == nil
×
818
  end
819
  
820
  defp evaluate_condition_internal(item, {field, :nil}) do
821
    Map.get(item, field) == nil
×
822
  end
823
  
824
  defp evaluate_condition_internal(item, {:similar_to, {module, function, arity}}) do
825
    # Handle special case where similar_to is the first element
826
    # This would typically compare the entire item/function to the target
827
    case Map.get(item, :mfa) || {Map.get(item, :module), Map.get(item, :function), Map.get(item, :arity)} do
×
828
      {^module, ^function, ^arity} -> true
×
829
      _ -> false
×
830
    end
831
  end
832
  
833
  defp evaluate_condition_internal(item, {:similarity_threshold, threshold}) do
834
    # Handle similarity threshold - would integrate with AI analysis
835
    case Map.get(item, :similarity_score) do
×
836
      nil -> false
×
837
      score -> score >= threshold
×
838
    end
839
  end
840
  
841
  defp evaluate_condition_internal(item, {:and, conditions}) do
842
    Enum.all?(conditions, &evaluate_condition_internal(item, &1))
2✔
843
  end
844
  
845
  defp evaluate_condition_internal(item, {:or, conditions}) do
846
    Enum.any?(conditions, &evaluate_condition_internal(item, &1))
2✔
847
  end
848
  
849
  defp evaluate_condition_internal(item, {:not, condition}) do
850
    not evaluate_condition_internal(item, condition)
2✔
851
  end
852
  
853
  defp evaluate_condition_internal(_item, _condition), do: false
×
854
  
855
  defp apply_ordering_internal(data, nil), do: data
×
856
  defp apply_ordering_internal(data, {field, :asc}) do
857
    Enum.sort_by(data, &Map.get(&1, field, 0))
2✔
858
  end
859
  defp apply_ordering_internal(data, {field, :desc}) do
860
    Enum.sort_by(data, &Map.get(&1, field, 0), :desc)
2✔
861
  end
862
  defp apply_ordering_internal(data, {:asc, field}) do
863
    Enum.sort_by(data, &Map.get(&1, field, 0))
×
864
  end
865
  defp apply_ordering_internal(data, {:desc, field}) do
866
    Enum.sort_by(data, &Map.get(&1, field, 0), :desc)
×
867
  end
868
  defp apply_ordering_internal(data, order_specs) when is_list(order_specs) do
869
    Enum.reduce(order_specs, data, fn order_spec, acc ->
1✔
870
      apply_ordering_internal(acc, order_spec)
2✔
871
    end)
872
  end
873
  
874
  defp apply_limit_offset_internal(data, nil, 0), do: data
×
875
  defp apply_limit_offset_internal(data, nil, offset), do: Enum.drop(data, offset)
1✔
876
  defp apply_limit_offset_internal(data, limit, 0), do: Enum.take(data, limit)
1✔
877
  defp apply_limit_offset_internal(data, limit, offset) do
878
    data |> Enum.drop(offset) |> Enum.take(limit)
1✔
879
  end
880
  
881
  defp apply_select_internal(data, :all), do: data
×
882
  defp apply_select_internal(data, fields) when is_list(fields) do
883
    Enum.map(data, fn item ->
×
884
      Map.take(item, fields)
×
885
    end)
886
  end
887
  
888
  defp check_cache(cache_key) do
889
    case :ets.lookup(@table_name, cache_key) do
7✔
890
      [{^cache_key, result, timestamp}] ->
891
        if System.monotonic_time(:millisecond) - timestamp < @cache_ttl do
×
892
          {:hit, result}
893
        else
894
          :ets.delete(@table_name, cache_key)
×
895
          :miss
896
        end
897
      
898
      [] -> :miss
7✔
899
    end
900
  end
901
  
902
  defp cache_result(cache_key, result) do
903
    timestamp = System.monotonic_time(:millisecond)
×
904
    :ets.insert(@table_name, {cache_key, result, timestamp})
×
905
  end
906
  
907
  defp cleanup_expired_cache() do
908
    current_time = System.monotonic_time(:millisecond)
×
909
    
910
    :ets.foldl(fn {key, _result, timestamp}, acc ->
×
911
      if current_time - timestamp >= @cache_ttl do
×
912
        :ets.delete(@table_name, key)
×
913
      end
914
      acc
×
915
    end, nil, @table_name)
916
  end
917
  
918
  defp update_performance_stats(state, execution_time) do
919
    current_stats = state.query_stats
×
920
    new_total = current_stats.total + 1
×
921
    new_avg = (current_stats.avg_time * current_stats.total + execution_time) / new_total
×
922
    
923
    updated_stats = %{
×
924
      total: new_total,
925
      avg_time: new_avg
926
    }
927
    
928
    %{state | query_stats: updated_stats}
×
929
  end
930
  
931
  defp add_execution_metadata(result, execution_time, query) do
932
    performance_score = cond do
×
933
      execution_time <= @simple_query_threshold -> :excellent
×
934
      execution_time <= @complex_query_threshold -> :good
×
935
      execution_time <= @complex_query_threshold * 2 -> :fair
×
936
      true -> :poor
×
937
    end
938
    
939
    metadata = %{
×
940
      execution_time_ms: execution_time,
941
      cache_hit: Map.get(result, :cache_hit, false),
942
      optimization_applied: query.optimization_hints || [],
×
943
      performance_score: performance_score,
944
      estimated_cost: query.estimated_cost
×
945
    }
946
    
947
    Map.put(result, :metadata, metadata)
×
948
  end
949
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

© 2025 Coveralls, Inc