2014-07-21 1 views
3

Mongoid 및 MongoDB v2.4.6을 사용하는 레일즈 응용 프로그램에서 루비를 사용하고 있습니다.쿼리 Mongo 임베디드 문서 크기

{ 
    "_id" : "76561198045636214", 
    "fragments" : [ 
    { 
     "id" : 76561198045636215, 
     "source_id" : "source1" 
    }, 
    { 
     "id" : 76561198045636216, 
     "source_id" : "source2" 
    }, 
    { 
     "id" : 76561198045636217, 
     "source_id" : "source2" 
    } 
    ] 
} 

내가 중복 source_ids와 조각이 포함 된 데이터베이스의 모든 레코드를 찾기 위해 노력하고 있어요 :

나는 다음과 같은 MongoDB의 구조, embeds_many 조각이 기록을 가지고있다.

저는 임베디드 문서를 쿼리 할 때 $ elemMatch를 사용해야한다고 확신합니다.

나는 작동하지만 중복을 제한하지 않습니다

Record.elem_match(fragments: {source_id: 'source2'}) 

을 시도했습니다.

은 그때 어떤 결과를 반환하지 않습니다 (그러나 유효한 쿼리입니다)
Record.elem_match(fragments: {source_id: 'source2', :source_id.with_size => 2}) 

을 시도했다. Mongoid가 생성하는 쿼리는 다음과 같습니다.

selector: {"fragments"=>{"$elemMatch"=>{:source_id=>"source2", "source_id"=>{"$size"=>2}}}} 

일단 제대로 작동하면 $ 크기가 1보다 커야합니다.

이것이 가능합니까? 내가 아주 가깝다고 느낀다. 이것은 일회성 정리 작업이므로 쿼리 성능이 그리 큰 문제는 아닙니다 (업데이트 할 수있는 수백만 개의 레코드가 있습니다!)

도움이 되었습니까?

나는 원하는 결과를 얻을 수 있었지만 테스트가 너무 느리다 (우리 프로덕션 시스템을 실행하는 데는 수주가 걸릴 것이다). 문제는 레코드 당 두 번 쿼리 (우리는 프로덕션에서 ~ 3000 만 레코드가 있음)입니다.

Record.where('fragments.source_id' => 'source2').each do |record| 
    query = record.fragments.where(source_id: 'source2') 
    if query.count > 1 
    # contains duplicates, delete all but latest 
    query.desc(:updated_at).skip(1).delete_all 
    end 
    # needed to trigger after_save filters 
    record.save! 
end 

답변

1

여기에 현재 접근 방식의 문제는 표준 MongoDB의 쿼리 형태가 실제로 "필터"어떤 방식으로 중첩 된 배열 문서를하지 않는 것입니다. 이것은 본질적으로 문서에서 "중복 된 항목 찾기"를 위해 필요한 것입니다.

이를 위해 MongoDB는 아마도 이것을 찾는 가장 좋은 접근 방법 인 집계 프레임 워크를 제공합니다. 관계형 문서를 다루는 기존의 "레일 (rails)"스타일에 맞춰진 것처럼 "mongoid"스타일의 직접적인 접근 방식은 없습니다.

당신은 당신의 클래스 모델에 .collection 접근을 통해 비록 "오토바이"양식에 액세스 할 수 있습니다

{ 
    "_id" : { "_id": "76561198045636214", "source_id": "source2" }, 
    "fragments": ["76561198045636216","76561198045636217"], 
    "count": 2 
} 

적어도 당신에게 뭔가를 준다 : 이런 식으로 당신에게 결과를 반환

Record.collection.aggregate([ 

    # Find arrays two elements or more as possibles 
    { "$match" => { 
     "$and" => [ 
      { "fragments" => { "$not" => { "$size" => 0 } } }, 
      { "fragments" => { "$not" => { "$size" => 1 } } } 
     ] 
    }}, 

    # Unwind the arrays to "de-normalize" as documents 
    { "$unwind" => "$fragments" }, 

    # Group back and get counts of the "key" values 
    { "$group" => { 
     "_id" => { "_id" => "$_id", "source_id" => "$fragments.source_id" }, 
     "fragments" => { "$push" => "$fragments.id" }, 
     "count" => { "$sum" => 1 } 
    }}, 

    # Match the keys found more than once 
    { "$match" => { "count" => { "$gte" => 2 } } } 
]) 

여기에서 "복제물"을 다루는 방법을 다루는 것

+0

와우 좋은 작품 Neil, 나는 완전히 내 자신에게있어주지 않을 것이다! 감사합니다 훌륭하게 작동;) – daveharris