[MONGODB] 몽구스의 채우기 후 쿼리
MONGODB몽구스의 채우기 후 쿼리
나는 일반적으로 몽구스와 MongoDB를 꽤 새로운 해요 내가 이런 일이 가능하면 알아내는 힘든 시간을 보내고있어 너무 :
Item = new Schema({
id: Schema.ObjectId,
dateCreated: { type: Date, default: Date.now },
title: { type: String, default: 'No Title' },
description: { type: String, default: 'No Description' },
tags: [ { type: Schema.ObjectId, ref: 'ItemTag' }]
});
ItemTag = new Schema({
id: Schema.ObjectId,
tagId: { type: Schema.ObjectId, ref: 'Tag' },
tagName: { type: String }
});
var query = Models.Item.find({});
query
.desc('dateCreated')
.populate('tags')
.where('tags.tagName').in(['funny', 'politics'])
.run(function(err, docs){
// docs is always empty
});
더 좋은 방법이있다 할 수 있습니까?
편집하다
혼란에 대한 사과. 난 할 노력하고있어 재미있는 태그 또는 정치 태그 중 하나를 포함하는 모든 항목을 얻을 수 있습니다.
편집하다
where 절없이 문서 :
[{
_id: 4fe90264e5caa33f04000012,
dislikes: 0,
likes: 0,
source: '/uploads/loldog.jpg',
comments: [],
tags: [{
itemId: 4fe90264e5caa33f04000012,
tagName: 'movies',
tagId: 4fe64219007e20e644000007,
_id: 4fe90270e5caa33f04000015,
dateCreated: Tue, 26 Jun 2012 00:29:36 GMT,
rating: 0,
dislikes: 0,
likes: 0
},
{
itemId: 4fe90264e5caa33f04000012,
tagName: 'funny',
tagId: 4fe64219007e20e644000002,
_id: 4fe90270e5caa33f04000017,
dateCreated: Tue, 26 Jun 2012 00:29:36 GMT,
rating: 0,
dislikes: 0,
likes: 0
}],
viewCount: 0,
rating: 0,
type: 'image',
description: null,
title: 'dogggg',
dateCreated: Tue, 26 Jun 2012 00:29:24 GMT
}, ... ]
where 절과 함께, 나는 하늘의 배열을 얻는다.
해결법
-
==============================
1.3.2보다 현대 MongoDB를의 큰하면 대부분의 경우 () .populate하는 대체로 $ 조회를 사용할 수 있습니다. 이것은 또한 ()는 실제로 "에뮬레이션"에서 "여러 쿼리"조인되는 않는 .populate 무엇에 반대 "서버"에 가입 실제로 수행하는 장점이있다.
3.2보다 현대 MongoDB를의 큰하면 대부분의 경우 () .populate하는 대체로 $ 조회를 사용할 수 있습니다. 이것은 또한 ()는 실제로 "에뮬레이션"에서 "여러 쿼리"조인되는 않는 .populate 무엇에 반대 "서버"에 가입 실제로 수행하는 장점이있다.
.populate은 () 정말 아닙니다 그래서 관계형 데이터베이스가 어떻게하는지의 의미에서 "참여". 반면에 $ 조회 연산자는 실제로 서버에서 작업을 수행하고, 더 많거나 적은 유사한 "LEFT 조인"하는 것입니다 :
Item.aggregate( [ { "$lookup": { "from": ItemTags.collection.name, "localField": "tags", "foreignField": "_id", "as": "tags" }}, { "$unwind": "$tags" }, { "$match": { "tags.tagName": { "$in": [ "funny", "politics" ] } } }, { "$group": { "_id": "$_id", "dateCreated": { "$first": "$dateCreated" }, "title": { "$first": "$title" }, "description": { "$first": "$description" }, "tags": { "$push": "$tags" } }} ], function(err, result) { // "tags" is now filtered by condition and "joined" } )
우리는 또한 원치 않는 항목을 제거하기 위해 배열에 $ 필터를 사용할 수있는 동안, 이것은 실제로는 $ 조회 둘 모두 $ 긴장을 풀고 달러 (A $) 정합 조건에 의해 다음과 같이의 특별한 조건에 대한 집계 파이프 라인 최적화에 가장 효율적인 형태입니다.
이것은 실제로 하나에 출시되는 세 개의 파이프 라인 단계가 발생합니다
{ "$lookup" : { "from" : "itemtags", "as" : "tags", "localField" : "tags", "foreignField" : "_id", "unwinding" : { "preserveNullAndEmptyArrays" : false }, "matching" : { "tagName" : { "$in" : [ "funny", "politics" ] } } }}
실제 조작은 "제 가입 콜렉션 필터"다음의 결과 "풀려"배열을 반환 이것은 매우 적합하다. 결과는 클라이언트가없는 것을 제약이다 16메가바이트의 BSON 한계를 아프게하지 않도록 두 가지 방법이 사용된다.
유일한 문제는 당신이 배열의 결과를 원하는 특히, "직관"어떤 방법으로 보이지만, 그게 원본 문서 양식으로 재구성으로 $ 그룹이 여기에 대해 어떤 점이다.
우리가 단순히이 시간에 실제로 서버가 사용하는 동일한 최종 구문 $를 조회 쓸 수없는 것이 유감입니다. IMHO,이 수정 될 수있는 감독이다. 하지만 지금은, 단순히 순서를 사용하여 작업과 최고의 성능과 확장 성을 가장 실행 가능한 옵션입니다 것입니다.
여기에 표시된 패턴이 상당히 인해 다른 단계가 모두 $ 조회 및 채우기의 행동 (에 일반적으로 내재되어있는 "LEFT이 가입"고에 실패한 $ 조회, 그것은이 있습니까 하나에 압연 얻을 방법에 최적화되어 있지만) 부정한다 언 와인드 여기에 $의 "최적의"사용에 의해 빈 배열을 보존하지 않는다. 당신은 preserveNullAndEmptyArrays 옵션을 추가 할 수 있지만, "최적화"순서는 기본적으로 위에서 설명한이을 Negate은 일반적으로 최적화에 결합 될 것 그대로 세 단계를 떠난다.
MongoDB를은 "하위 파이프 라인"표현을 허용 $ 조회의 "더 표현"양식 3.6 확장합니다. 어느뿐만 아니라 충족 유지의 목표 "LEFT JOIN을"하지만 여전히 최적의 쿼리가 반환 된 결과를 줄이고 훨씬 더 단순화 된 구문을 할 수 있습니다 :
Item.aggregate([ { "$lookup": { "from": ItemTags.collection.name, "let": { "tags": "$tags" }, "pipeline": [ { "$match": { "tags": { "$in": [ "politics", "funny" ] }, "$expr": { "$in": [ "$_id", "$$tags" ] } }} ] }} ])
EXPR 경기하기 위해 사용되는 $는 "외국"값 "로컬"값이 MongoDB를 원래 $ 조회 구문 이제 "내부적으로"무엇을 실제로 선언했다. 이 형태로 표현함으로써 우리는 "하위 파이프 라인"자신 내에서 초기 $ 일치 식을 조정할 수 있습니다.
사실, 진정한 "통합 파이프 라인"당신은 당신이 다른 관련 컬렉션에 $ 조회의 "중첩"수준을 포함하여이 "하위 파이프 라인"표현에서 집계 파이프 라인으로 할 수있는 일에 대해 할 수 있습니다.
이 가득에서 또한 사용은 약간의 문제는 여기 요구하시는의 범위를 벗어나지 만도 "중첩 된 인구"과 관련하여 다음 $ 조회의 새로운 사용 패턴이 훨씬 동일 할 수 있도록하고, "많은"더 강력한 용법.
다음 모델에 고정 방법을 사용한 예이다. 정적 메소드가 호출을 구현하면 간단하게 :
Item.lookup( { path: 'tags', query: { 'tags.tagName' : { '$in': [ 'funny', 'politics' ] } } }, callback )
심지어 좀 더 현대적인 것으로 강화하는 것은이된다 :
let results = await Item.lookup({ path: 'tags', query: { 'tagName' : { '$in': [ 'funny', 'politics' ] } } })
매우 유사한 구조 () .populate 할 수있게하지만, 실제로 대신 서버에 참여하고있어. 완성도를 들어, 사용은 여기에 반환 된 데이터는 모두 부모와 자식 경우에 따라서에서 몽구스 문서 인스턴스에 다시 던진다.
그것은 아주 사소한 및 적응하거나 대부분의 경우에 대해있는 그대로 사용하기 쉽습니다.
const async = require('async'), mongoose = require('mongoose'), Schema = mongoose.Schema; mongoose.Promise = global.Promise; mongoose.set('debug', true); mongoose.connect('mongodb://localhost/looktest'); const itemTagSchema = new Schema({ tagName: String }); const itemSchema = new Schema({ dateCreated: { type: Date, default: Date.now }, title: String, description: String, tags: [{ type: Schema.Types.ObjectId, ref: 'ItemTag' }] }); itemSchema.statics.lookup = function(opt,callback) { let rel = mongoose.model(this.schema.path(opt.path).caster.options.ref); let group = { "$group": { } }; this.schema.eachPath(p => group.$group[p] = (p === "_id") ? "$_id" : (p === opt.path) ? { "$push": `$${p}` } : { "$first": `$${p}` }); let pipeline = [ { "$lookup": { "from": rel.collection.name, "as": opt.path, "localField": opt.path, "foreignField": "_id" }}, { "$unwind": `$${opt.path}` }, { "$match": opt.query }, group ]; this.aggregate(pipeline,(err,result) => { if (err) callback(err); result = result.map(m => { m[opt.path] = m[opt.path].map(r => rel(r)); return this(m); }); callback(err,result); }); } const Item = mongoose.model('Item', itemSchema); const ItemTag = mongoose.model('ItemTag', itemTagSchema); function log(body) { console.log(JSON.stringify(body, undefined, 2)) } async.series( [ // Clean data (callback) => async.each(mongoose.models,(model,callback) => model.remove({},callback),callback), // Create tags and items (callback) => async.waterfall( [ (callback) => ItemTag.create([{ "tagName": "movies" }, { "tagName": "funny" }], callback), (tags, callback) => Item.create({ "title": "Something","description": "An item", "tags": tags },callback) ], callback ), // Query with our static (callback) => Item.lookup( { path: 'tags', query: { 'tags.tagName' : { '$in': [ 'funny', 'politics' ] } } }, callback ) ], (err,results) => { if (err) throw err; let result = results.pop(); log(result); mongoose.disconnect(); } )
또는 노드 8.x의과와 비동기 / await를하고 추가 종속성 위 현대 조금 :
const { Schema } = mongoose = require('mongoose'); const uri = 'mongodb://localhost/looktest'; mongoose.Promise = global.Promise; mongoose.set('debug', true); const itemTagSchema = new Schema({ tagName: String }); const itemSchema = new Schema({ dateCreated: { type: Date, default: Date.now }, title: String, description: String, tags: [{ type: Schema.Types.ObjectId, ref: 'ItemTag' }] }); itemSchema.statics.lookup = function(opt) { let rel = mongoose.model(this.schema.path(opt.path).caster.options.ref); let group = { "$group": { } }; this.schema.eachPath(p => group.$group[p] = (p === "_id") ? "$_id" : (p === opt.path) ? { "$push": `$${p}` } : { "$first": `$${p}` }); let pipeline = [ { "$lookup": { "from": rel.collection.name, "as": opt.path, "localField": opt.path, "foreignField": "_id" }}, { "$unwind": `$${opt.path}` }, { "$match": opt.query }, group ]; return this.aggregate(pipeline).exec().then(r => r.map(m => this({ ...m, [opt.path]: m[opt.path].map(r => rel(r)) }) )); } const Item = mongoose.model('Item', itemSchema); const ItemTag = mongoose.model('ItemTag', itemTagSchema); const log = body => console.log(JSON.stringify(body, undefined, 2)); (async function() { try { const conn = await mongoose.connect(uri); // Clean data await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove())); // Create tags and items const tags = await ItemTag.create( ["movies", "funny"].map(tagName =>({ tagName })) ); const item = await Item.create({ "title": "Something", "description": "An item", tags }); // Query with our static const result = (await Item.lookup({ path: 'tags', query: { 'tags.tagName' : { '$in': [ 'funny', 'politics' ] } } })).pop(); log(result); mongoose.disconnect(); } catch (e) { console.error(e); } finally { process.exit() } })()
그리고 MongoDB를 3.6에서 위쪽으로, 심지어는 $의 긴장을 풀고 $ 그룹 건물없이 :
const { Schema, Types: { ObjectId } } = mongoose = require('mongoose'); const uri = 'mongodb://localhost/looktest'; mongoose.Promise = global.Promise; mongoose.set('debug', true); const itemTagSchema = new Schema({ tagName: String }); const itemSchema = new Schema({ title: String, description: String, tags: [{ type: Schema.Types.ObjectId, ref: 'ItemTag' }] },{ timestamps: true }); itemSchema.statics.lookup = function({ path, query }) { let rel = mongoose.model(this.schema.path(path).caster.options.ref); // MongoDB 3.6 and up $lookup with sub-pipeline let pipeline = [ { "$lookup": { "from": rel.collection.name, "as": path, "let": { [path]: `$${path}` }, "pipeline": [ { "$match": { ...query, "$expr": { "$in": [ "$_id", `$$${path}` ] } }} ] }} ]; return this.aggregate(pipeline).exec().then(r => r.map(m => this({ ...m, [path]: m[path].map(r => rel(r)) }) )); }; const Item = mongoose.model('Item', itemSchema); const ItemTag = mongoose.model('ItemTag', itemTagSchema); const log = body => console.log(JSON.stringify(body, undefined, 2)); (async function() { try { const conn = await mongoose.connect(uri); // Clean data await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove())); // Create tags and items const tags = await ItemTag.insertMany( ["movies", "funny"].map(tagName => ({ tagName })) ); const item = await Item.create({ "title": "Something", "description": "An item", tags }); // Query with our static let result = (await Item.lookup({ path: 'tags', query: { 'tagName': { '$in': [ 'funny', 'politics' ] } } })).pop(); log(result); await mongoose.disconnect(); } catch(e) { console.error(e) } finally { process.exit() } })()
-
==============================
2.당신을 위해 무엇을 요구하는 것은 직접 지원이 아니라 쿼리 반환 후 다른 필터 단계를 추가함으로써 달성 될 수있다.
당신을 위해 무엇을 요구하는 것은 직접 지원이 아니라 쿼리 반환 후 다른 필터 단계를 추가함으로써 달성 될 수있다.
첫째, .populate ( '태그', 널 (null) {태그 이름 : {$에서 : [ '재미', '정치']}})는 태그 문서를 필터링해야 할 일을 확실히이다. 다음 쿼리 반환 후 수동으로 채우기 기준과 일치하는 것이 어떤 태그 문서가없는 문서를 필터링해야합니다. 뭔가 같은 :
query.... .exec(function(err, docs){ docs = docs.filter(function(doc){ return doc.tags.length; }) // do stuff with docs });
-
==============================
3.교체 시도
교체 시도
.populate('tags').where('tags.tagName').in(['funny', 'politics'])
으로
.populate( 'tags', null, { tagName: { $in: ['funny', 'politics'] } } )
-
==============================
4.업데이트 : 코멘트에서 봐 주시기 바랍니다 -이 "답"을 삭제하지 않도록이 답변이 제대로 질문에 일치하지 않는,하지만 어쩌면 그것은 우연히 사용자의 다른 질문에 대한 답변 (I 때문에 upvotes의 생각)
업데이트 : 코멘트에서 봐 주시기 바랍니다 -이 "답"을 삭제하지 않도록이 답변이 제대로 질문에 일치하지 않는,하지만 어쩌면 그것은 우연히 사용자의 다른 질문에 대한 답변 (I 때문에 upvotes의 생각)
첫째 :이 질문은 정말 오래된 알고 있지만 정확히이 문제에 대한 검색이 SO 포스트는 구글 항목 # 1이었다. 나는 docs.filter 버전 (허용 대답)를 구현하지만 몽구스 v4.6.0 워드 프로세서에서 읽을 때 우리가 간단하게 사용할 수 있도록 :
Item.find({}).populate({ path: 'tags', match: { tagName: { $in: ['funny', 'politics'] }} }).exec((err, items) => { console.log(items.tags) // contains only tags where tagName is 'funny' or 'politics' })
이 미래의 검색 시스템 사용자가 도움이되기를 바랍니다.
-
==============================
5.최근 자신을 동일한 문제를 가진 후, 나는 다음과 같은 해결책을 마련했습니다 :
최근 자신을 동일한 문제를 가진 후, 나는 다음과 같은 해결책을 마련했습니다 :
첫째, 모든 태그 이름은 '재미'중 하나입니다 ItemTags 또는 '정치'를 찾아 ItemTag의 _ids의 배열을 반환합니다.
그런 다음, 태그 배열의 모든 ItemTag의 _ids를 포함하는 항목을 찾을 수
ItemTag .find({ tagName : { $in : ['funny','politics'] } }) .lean() .distinct('_id') .exec((err, itemTagIds) => { if (err) { console.error(err); } Item.find({ tag: { $all: itemTagIds} }, (err, items) => { console.log(items); // Items filtered by tagName }); });
-
==============================
6.@aaronheckmann의 대답은 나를 위해 일하지만 반환 doc.tags.length을 대체했다; 반환 doc.tags에 = 널! 해당 필드가 null이 포함되어 있기 때문에 채우기 내부에 기록 된 조건에 일치하지 않는 경우. 최종 코드 그래서 :
@aaronheckmann의 대답은 나를 위해 일하지만 반환 doc.tags.length을 대체했다; 반환 doc.tags에 = 널! 해당 필드가 null이 포함되어 있기 때문에 채우기 내부에 기록 된 조건에 일치하지 않는 경우. 최종 코드 그래서 :
query.... .exec(function(err, docs){ docs = docs.filter(function(doc){ return doc.tags != null; }) // do stuff with docs });
from https://stackoverflow.com/questions/11303294/querying-after-populate-in-mongoose by cc-by-sa and MIT license
'MONGODB' 카테고리의 다른 글
[MONGODB] 두 번 중첩 된 배열에서 찾기 MongoDB를 (0) | 2019.11.29 |
---|---|
[MONGODB] MongoDB의 15 분의 시간 간격에 의한 그룹 결과 (0) | 2019.11.29 |
[MONGODB] 방법 "과 같은"로 MongoDB의 쿼리를 하는가? (0) | 2019.11.29 |
[MONGODB] 두 날짜 사이의 객체를 찾기 MongoDB를 (0) | 2019.11.29 |
[MONGODB] MongoDB를 가진 중첩 된 배열을 업데이트 (0) | 2019.11.29 |