Node.js 同步和x27的正确设置;使用RxJS使用可观测数据对大型数据集(系统到系统)进行数据挖掘
我正在尝试使用NodeJS应用程序将数据从一个系统同步到另一个系统,以帮助以事务方式跟踪同步过程。下面是我试图完成的一个修改后的片段。间隔时间和抽头仅用于记录和测试目的。到目前为止,它正在做我想做的事情,但感觉不对或似乎不对 我正在尝试做的事情的总体示意图: 1.注意:最左边的项目是kick starter操作(UI/最终用户操作&调用getActiveCourses的内部间隔) *编辑:我的主要问题是,如何使用RxJS Observables创建一个类似队列管理状态的对象,以允许单个项目在完成后进行自我同步和自我处置 *编辑2:我不是在寻求关于设计整个系统的帮助,而是在如何正确地设置一个可以管理课程列表的Observable,然后使用另一个Observable监视器来过滤特定课程方面。在这些过滤的课程上,每个课程都应该是自己的可观察到的,以同步与自身相关的数据,然后自行取消订阅Node.js 同步和x27的正确设置;使用RxJS使用可观测数据对大型数据集(系统到系统)进行数据挖掘,node.js,rxjs,Node.js,Rxjs,我正在尝试使用NodeJS应用程序将数据从一个系统同步到另一个系统,以帮助以事务方式跟踪同步过程。下面是我试图完成的一个修改后的片段。间隔时间和抽头仅用于记录和测试目的。到目前为止,它正在做我想做的事情,但感觉不对或似乎不对 我正在尝试做的事情的总体示意图: 1.注意:最左边的项目是kick starter操作(UI/最终用户操作&调用getActiveCourses的内部间隔) *编辑:我的主要问题是,如何使用RxJS Observables创建一个类似队列管理状态的对象,以允许单个项目在完成
因此,在研究了扁平化策略之后,我能够为我所面临的问题创建一个有效的解决方案。总的问题是,我订阅的是一个在一个可观察范围内的一个可观察范围。这基本上是一个合并地图策略,而不是我想要的。事实证明,我需要执行一个switchMap策略,同时使用scan操作符根据当前/累积的数据检查传入的数据 这篇文章真正帮助我了解了发生的事情:
我想你需要把你的问题缩小一点。我很乐意帮助你使用RxJS,但是你要求设计一个完整的系统。。。你能把你的问题缩小到处理管道吗?
const courseProcessQueue = new BehaviorSubject({});
const processQueuedCourses = new Subject();
processQueuedCourses
.subscribe((data: any) => // problematic area!!!!!!
data.pipe(
filter((d: any) => d.length <= 2),
tap(loadCourseEnrollments),// end of problematic area
)
.subscribe(d => console.log('[processQueuedCourses]', d))
);
processQueuedCourses.next(courseProcessQueue);
interval(500)
.pipe(
map(loadActiveCourses),
flatMap(data => data),
map(courseSyncQueue),
)
.subscribe(() => console.log('pushed new course queue state'));
courseProcessQueue.subscribe((courses: any) =>
console.log('[courseProcessQueue]', courses.length)
);
function loadActiveCourses() {
let limit = Math.floor(Math.random() * (10 - 1 + 1) + 1)
return from(getActiveCourses('externalId id', limit));
}
function courseSyncQueue(courses: any) {
courseProcessQueue.next(courses);
}
async function loadCourseEnrollments(courses: any) {
console.log('PROCESSING ENROLLMENTS!!!!!!!!')
courses.map(course => console.log('PROCESSED:', course.externalId));
}
[getActiveCourses()]
PROCESSING ENROLLMENTS!!!!!!!!
PROCESSED: Course-001
PROCESSED: Course-002
[ processQueuedCourses] [ { id: '1',
externalId: 'Course-001' },
{ id: '2',
externalId: 'Course-002' } ]
[courseProcessQueue] 2
pushed new course queue state
[getActiveCourses()]
[courseProcessQueue] 8
pushed new course queue state
[courseProcessQueue] 9
pushed new course queue state
import * as mongoose from 'mongoose';
import * as bluebird from 'bluebird';
import { interval, from, BehaviorSubject, timer } from 'rxjs';
import { map, switchMap, scan } from 'rxjs/operators';
import { mongooseConfig } from './config';
import { getActiveCourses, getCourseById } from './logic/data-service';
import { Course } from './models/course';
// import { inspect } from 'util';
(<any>mongoose).Promise = bluebird;
mongoose.connection.once('open', () => {
mongoose.connection.on('error', (err: any) => { console.log(err); });
});
mongoose.connect(mongooseConfig.database, mongooseConfig.options);
/**
* Test for updates to a course
*/
Course.findOneAndUpdate(
{id: '_69107_1'},
{'availability.available': 'N'},
{upsert: true}
)
.select('-_id id externalId updatedAt')
.exec()
.then(console.log);
Course.findOneAndUpdate(
{id: '_69107_1'},
{'availability.available': 'Y'},
{upsert: true}
)
.select('-_id id externalId updatedAt')
.exec()
.then(console.log);
/**
* END TEST
*/
/**
* getActiveCoursesFromDB => from() => switchMap() => BehaviorSubject()
*/
// just use 3 seconds for now, update to 15 * (60 * 1000) later
const refresh15MinutesAgo = 3000;
const filteredCourses = new BehaviorSubject([]);
// get the active courses that are stale test
interval(1000).pipe(
map(() => from(getActiveCourses({select: '-_id id externalId updatedAt', limit: 3}))),
switchMap(courses => courses),
map(courses => courses.filter(course => course.updatedAt < Date.now() - refresh15MinutesAgo))
).subscribe(setFilteredCourses);
filteredCourses.pipe(
scan((acc, curr) => {
// ensure there is a current obj
if (curr) {
// if the current obj is not an array then check it directly
if (!Array.isArray(curr)) {
if (!containsObject(curr, acc)) {
return [...new Set([...acc, curr])];
}
} else {
// otherwise process only the ones that are not in the current stack
return [...new Set([...acc, ...curr.filter(c => !containsObject(c, acc))])];
}
}
// either first iteration or curr was undefined
return acc;
})
).subscribe(logOut);
//test to inject a course and update, simulating a front end override call
timer(6000).pipe(
map(() => from(getCourseById('4', {select: '-_id id externalId updatedAt'}))),
switchMap(c => c)
).subscribe(setFilteredCourses);
// helper function to push data into the BehaviorSubject
function setFilteredCourses(data) {
filteredCourses.next(data);
}
function logOut(output: any) {
console.log('[logOut]', output);
}
// helper function to test if an object is in our list
function containsObject(obj, list) {
const ids = list.filter(c => c.id === obj.id);
if (ids.length > 0) {
return true;
}
return false;
}
//creating an update for test
{ id: '3',
externalId: 'Course-003',
updatedAt: 2018-09-11T02:00:39.986Z }
{ id: '3',
externalId: 'Course-003',
updatedAt: 2018-09-11T02:01:31.710Z }
//time passes
[getActiveCourses()]
[logOut] [ { id: '1',
externalId: 'Course-001',
updatedAt: 2018-09-07T16:45:58.295Z },
{ id: '2',
externalId: 'Course-002',
updatedAt: 2018-09-07T16:45:58.295Z } ]
[logOut] [ { id: '1',
externalId: 'Course-001',
updatedAt: 2018-09-07T16:45:58.295Z },
{ id: '2',
externalId: 'Course-002',
updatedAt: 2018-09-07T16:45:58.295Z },
{ id: '3',
externalId: 'Course-003',
updatedAt: 2018-09-11T02:01:31.710Z } ]
//more time passes, 4 was injected into the stack/queue
[getActiveCourses()]
[logOut] [ { id: '1',
externalId: 'Course-001',
updatedAt: 2018-09-07T16:45:58.295Z },
{ id: '2',
externalId: 'Course-002',
updatedAt: 2018-09-07T16:45:58.295Z },
{ id: '3',
externalId: 'Course-003',
updatedAt: 2018-09-11T02:01:31.710Z },
{ id: '4',
externalId: 'Course-004',
updatedAt: 2018-09-07T16:45:58.295Z } ]