검색

Aeca는 검색을 위한 많은 연산자가 준비되어 있고 이를 이용하여 검색하는 방법에 대해서 설명합니다. 여기서는 그 중 점수화하지 않고 일치/불일치 여부만 판단하는 필터 검색에 대한 내용을 다루고 있습니다. 이러한 필터 검색은 주로 정형 데이터 검색에 적합합니다.

만약 단어의 일부, 벡터 검색 등을 원하신다면 전문 검색을 참고해 주세요.

사전 준비

검색을 위한 데이터를 입력하는 과정을 설명합니다. 예제로 Kaggle (opens in a new tab)에 공개된 Car information dataset (opens in a new tab)을 사용합니다.

이 데이터셋의 다음과 같은 필드를 가지고 있습니다.

  • name: Unique identifier for each automobile.
  • mpg: Fuel efficiency measured in miles per gallon.
  • cylinders: Number of cylinders in the engine.
  • displacement: Engine displacement, indicating its size or capacity.
  • horsepower: Power output of the engine.
  • weight: Weight of the automobile.
  • acceleration: Capability to increase speed, measured in seconds.
  • model_year: Year of manufacture for the automobile model.
  • origin: Country or region of origin for each automobile.

이제 예제 데이터를 가져옵니다.

import pandas as pd
 
df = pd.read_csv("automobile.csv")
df = df.reset_index()
df = df.rename(columns={"index": "doc_id"})
print(df.head(3))

실행 결과는 다음과 같습니다.

실행 결과
   doc_id                       name   mpg  cylinders  displacement  \
0       0  chevrolet chevelle malibu  18.0          8         307.0
1       1          buick skylark 320  15.0          8         350.0
2       2         plymouth satellite  18.0          8         318.0

   horsepower  weight  acceleration  model_year origin
0       130.0    3504          12.0          70    usa
1       165.0    3693          11.5          70    usa
2       150.0    3436          11.0          70    usa

그리고 데이터 타입을 확인해 보면

print(df.dtypes)

적절한 데이터 타입을 가지고 있는 것을 확인할 수 있습니다. 검색 과정에 영향을 받기 때문에 데이터 삽입 전 의도에 맞는 데이터 타입을 가지고 있는지 확인하는 것이 좋습니다.

실행 결과
doc_id            int64
name             object
mpg             float64
cylinders         int64
displacement    float64
horsepower      float64
weight            int64
acceleration    float64
model_year        int64
origin           object
dtype: object

이어서 데이터 입력과 관리에서 설명한 내용과 같이 컬렉션을 생성하고 데이터를 입력합니다.

from aeca import DocumentDB
 
doc_db = DocumentDB(channel)
 
indexes = [
    {
        "index_type": "kPrimaryKey",
        "fields": ["doc_id"]
    }
]
collection_name = "car_info"
doc_db.create_collection(collection_name, indexes=indexes)
doc_db.insert(collection_name, df.to_dict(orient="records"))
 
df = doc_db.find(collection_name, {"$limit": 10})
print(df.head(3))
print(df.dtypes)

예제를 단순화 하기 위해서 색인 정의는 제외되어 있습니다. 빠른 검색을 위해서 색인 정의를 위한 스키마를 참고하여 색인을 생성할 수 있습니다.

입력된 데이터는 아래와 같이 확인할 수 있습니다.

실행 결과
   acceleration  cylinders  displacement  doc_id  horsepower  model_year  \
0          12.0          8         307.0       0       130.0          70
1          11.5          8         350.0       1       165.0          70
2          11.0          8         318.0       2       150.0          70

    mpg                       name origin  weight
0  18.0  chevrolet chevelle malibu    usa    3504
1  15.0          buick skylark 320    usa    3693
2  18.0         plymouth satellite    usa    3436
acceleration           float64
cylinders                Int32
displacement           float64
doc_id                   Int32
horsepower             float64
model_year               Int32
mpg                    float64
name            string[python]
origin          string[python]
weight                   Int32
dtype: object

쿼리 작성

사전 준비를 통해 입력된 데이터에서 cylinders가 6개이고 horsepower가 100마력 이상인 자동차를 찾아보도록 하겠습니다. 그리고 name, horsepower, cylinders, model_year를 출력하고 마력을 기준으로 내림차순 정렬을 하고자 합니다.

이 조건은 다음과 같이 표현할 수 있습니다.

columns = [
    "name",
    "horsepower",
    "cylinders",
    "model_year"
]
query = [
    {
        "cylinders": 6,
        "horsepower": {
            "$ge": 100
        },
        "$project": columns,
        "$sort": [{"horsepower": "desc"}]
    }
]
df = doc_db.find(collection_name, query, columns=columns)
print(df.head(10))

실행 결과는 다음과 같습니다.

실행 결과
                              name  horsepower  cylinders  model_year
0  buick regal sport coupe (turbo)       165.0          6          78
1                    peugeot 604sl       133.0          6          78
2                    datsun 280-zx       132.0          6          80
3                      volvo 264gl       125.0          6          78
4                   toyota mark ii       122.0          6          73
5               mercedes-benz 280s       120.0          6          76
6                datsun 810 maxima       120.0          6          81
7                  amc concord d/l       120.0          6          78
8                  toyota cressida       116.0          6          81
9               chevrolet citation       115.0          6          79

쿼리 조건을 하나씩 살펴보면, 아래 조건은 cylinders가 6인 항목을 찾습니다.

"cylinders": 6

이는 비교 연산이며 아래의 쿼리와 의미상 같습니다.

"cylinders": {"$eq": 6}

이어서 다음 조건은 horsepower가 100보다 크거나 같은 값을 찾고 있습니다.

"horsepower": {
    "$ge": 100
}

다음과 같이 $project를 연산자를 사용하여 columns에서 정의한 칼럼만 가져오도록 설정했습니다.

"$project": columns

이어서 $sort 연산자를 사용하여 선택된 결과를 내림차순(desc) 정렬합니다.

"$sort": [{"horsepower": "desc"}]

마지막으로 DocumentDB.find 함수를 사용하여 검색을 요청합니다.

df = doc_db.find(collection_name, query, columns=columns)

파이프라인

위의 검색 결과에서 model_year가 80년 이후로 생산된 자동차를 검색하도록 수정하겠습니다. 물론 기존 쿼리를 수정하여 원하는 결과를 얻을 수 있지만 여기서는 Aeca에서 지원하는 쿼리 파이프라인을 사용합니다.

아래의 코드와 같이 기존 쿼리에 쿼리를 추가했습니다.

query = [
    {
        "cylinders": 6,
        "horsepower": {
            "$ge": 100
        },
        "$project": columns,
        "$sort": [{"horsepower": "desc"}]
    },
    {
        "model_year": {
            "$ge": 80
        }
    }
]
df = doc_db.find(collection_name, query, columns=columns)
print(df)

실행 결과는 의도와 같이 출력되는 것을 확인할 수 있습니다.

실행 결과
                    name  horsepower  cylinders  model_year
0          datsun 280-zx       132.0          6          80
1      datsun 810 maxima       120.0          6          81
2        toyota cressida       116.0          6          81
3         ford granada l       112.0          6          82
4     chevrolet citation       110.0          6          81
5          buick century       110.0          6          81
6  buick century limited       110.0          6          82

파이프라인은 다음과 같이 동작합니다.

  • 입력된 쿼리를 순서대로 실행
  • 쿼리는 이전 검색 결과를 기반으로 실행

위와 같은 특성이 있기 때문에 만약 파이프라인의 첫번째 쿼리에서 "$project": ["name", "horsepower", "cylinders"]로 전달하여 model_year가 이전 쿼리 검색 결과에 없었다면 위의 쿼리는 실행되지 않습니다.

첫번째 파이프에 대해서만 색인을 참조하며 두번째 이후의 파이프는 필터로써 동작합니다.

연산자

Aeca는 검색을 위한 다양한 기능을 제공하고 있습니다. 자세한 내용은 연산자 문서를 참고해 주세요.